diff --git a/.circleci/config.yml b/.circleci/config.yml
index f2c743e4525cf..cb44edca71a84 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -501,6 +501,9 @@ workflows:
- "-r=www-modern --env=development --variant=true"
- "-r=www-modern --env=production --variant=true"
+ # Codemods
+ - "--project=codemods -r=experimental"
+
# TODO: Test more persistent configurations?
- '-r=stable --env=development --persistent'
- '-r=experimental --env=development --persistent'
diff --git a/.gitignore b/.gitignore
index 6ec345e172e5e..f9d363c0c49d7 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,5 @@
.DS_STORE
+dist
node_modules
scripts/flow/*/.flowconfig
.flowconfig
diff --git a/babel.config.js b/babel.config.js
index f8a28b20cc87d..eb9480d309387 100644
--- a/babel.config.js
+++ b/babel.config.js
@@ -1,6 +1,10 @@
'use strict';
module.exports = {
+ presets: [
+ ['@babel/preset-env', {targets: {node: 'current'}}],
+ '@babel/preset-typescript',
+ ],
plugins: [
'@babel/plugin-syntax-jsx',
'@babel/plugin-transform-flow-strip-types',
diff --git a/codemods/ref-to-arrow-function-ref/.codemodrc.json b/codemods/ref-to-arrow-function-ref/.codemodrc.json
new file mode 100644
index 0000000000000..b8876e28e65e6
--- /dev/null
+++ b/codemods/ref-to-arrow-function-ref/.codemodrc.json
@@ -0,0 +1,15 @@
+{
+ "$schema": "https://codemod-utils.s3.us-west-1.amazonaws.com/configuration_schema.json",
+ "version": "1.0.3",
+ "private": false,
+ "name": "react/19/ref-to-arrow-function",
+ "engine": "jscodeshift",
+ "meta": {
+ "tags": ["react", "migration"],
+ "git": "https://github.com/codemod-com/react/tree/codemods-v19/codemods/ref-to-arrow-function-ref"
+ },
+ "applicability": {
+ "from": [["react", "<=", "18"]],
+ "to": [["react", "=", "19"]]
+ }
+}
diff --git a/codemods/ref-to-arrow-function-ref/README.md b/codemods/ref-to-arrow-function-ref/README.md
new file mode 100644
index 0000000000000..3038b6e32533b
--- /dev/null
+++ b/codemods/ref-to-arrow-function-ref/README.md
@@ -0,0 +1,14 @@
+This codemod migrates string refs (deprecated) to callback refs.
+
+## Before
+
+```ts
+< div ref = 'refName' / > ;
+```
+
+## After
+
+```ts
+< div ref = { ref => this.refs.refName = ref }
+/>
+```
diff --git a/codemods/ref-to-arrow-function-ref/__testfixtures__/fixture1.input.tsx b/codemods/ref-to-arrow-function-ref/__testfixtures__/fixture1.input.tsx
new file mode 100644
index 0000000000000..0f45e5e119037
--- /dev/null
+++ b/codemods/ref-to-arrow-function-ref/__testfixtures__/fixture1.input.tsx
@@ -0,0 +1 @@
+
{
+ this.refs.refName = ref;
+ }}
+/>;
diff --git a/codemods/ref-to-arrow-function-ref/__tests__/test.ts b/codemods/ref-to-arrow-function-ref/__tests__/test.ts
new file mode 100644
index 0000000000000..d3b1634a512a5
--- /dev/null
+++ b/codemods/ref-to-arrow-function-ref/__tests__/test.ts
@@ -0,0 +1,47 @@
+import assert from "node:assert";
+import { readFile } from "node:fs/promises";
+import { join } from "node:path";
+import jscodeshift, { type API } from "jscodeshift";
+import { describe, it } from "vitest";
+import transform from "../src/index.js";
+
+const buildApi = (parser: string | undefined): API => ({
+ j: parser ? jscodeshift.withParser(parser) : jscodeshift,
+ jscodeshift: parser ? jscodeshift.withParser(parser) : jscodeshift,
+ stats: () => {
+ console.error(
+ "The stats function was called, which is not supported on purpose",
+ );
+ },
+ report: () => {
+ console.error(
+ "The report function was called, which is not supported on purpose",
+ );
+ },
+});
+
+describe("react/19/ref-to-arrow-function", () => {
+ it("test #1", async () => {
+ const INPUT = await readFile(
+ join(__dirname, "..", "__testfixtures__/fixture1.input.tsx"),
+ "utf-8",
+ );
+ const OUTPUT = await readFile(
+ join(__dirname, "..", "__testfixtures__/fixture1.output.tsx"),
+ "utf-8",
+ );
+
+ const actualOutput = transform(
+ {
+ path: "index.js",
+ source: INPUT,
+ },
+ buildApi("tsx"),
+ );
+
+ assert.deepEqual(
+ actualOutput?.replace(/\s/gm, ""),
+ OUTPUT.replace(/\s/gm, ""),
+ );
+ });
+});
diff --git a/codemods/ref-to-arrow-function-ref/package.json b/codemods/ref-to-arrow-function-ref/package.json
new file mode 100644
index 0000000000000..a532b86afd5e6
--- /dev/null
+++ b/codemods/ref-to-arrow-function-ref/package.json
@@ -0,0 +1,19 @@
+{
+ "name": "ref-to-arrow-function",
+ "author": "codemod",
+ "dependencies": {},
+ "devDependencies": {
+ "@types/node": "20.9.0",
+ "typescript": "5.2.2",
+ "vitest": "^1.0.1",
+ "jscodeshift": "^0.15.1",
+ "@types/jscodeshift": "^0.11.10"
+ },
+ "scripts": {
+ "test": "vitest run",
+ "test:watch": "vitest watch"
+ },
+ "license": "MIT",
+ "files": ["README.md", ".codemodrc.json", "./dist/index.cjs"],
+ "type": "module"
+}
diff --git a/codemods/ref-to-arrow-function-ref/src/index.ts b/codemods/ref-to-arrow-function-ref/src/index.ts
new file mode 100644
index 0000000000000..cb49309041311
--- /dev/null
+++ b/codemods/ref-to-arrow-function-ref/src/index.ts
@@ -0,0 +1,81 @@
+import type { API, FileInfo, Options } from "jscodeshift";
+export default function transform(
+ file: FileInfo,
+ api: API,
+ options?: Options,
+): string | undefined {
+ const j = api.jscodeshift;
+ const root = j(file.source);
+
+ // Helper function to preserve comments
+ function replaceWithComments(path, newNode) {
+ // If the original node had comments, add them to the new node
+ if (path.node.comments) {
+ newNode.comments = path.node.comments;
+ }
+
+ // Replace the node
+ j(path).replaceWith(newNode);
+ }
+
+ // Find JSX elements with ref attribute
+ root
+ // Find JSX elements with a ref attribute
+ .find(j.JSXElement)
+ .forEach((path) => {
+ // Get the ref name
+ const refName = path.node.openingElement.attributes.find(
+ (attr) => attr?.name?.name === "ref",
+ )?.value?.value;
+
+ if (typeof refName !== "string") {
+ return;
+ }
+
+ // Create new ref attribute
+ const newRefAttr = j.jsxAttribute(
+ j.jsxIdentifier("ref"),
+ j.jsxExpressionContainer(
+ j.arrowFunctionExpression(
+ [j.jsxIdentifier("ref")],
+ j.blockStatement([
+ j.expressionStatement(
+ j.assignmentExpression(
+ "=",
+ j.memberExpression(
+ j.memberExpression(
+ j.thisExpression(),
+ j.identifier("refs"),
+ ),
+ j.identifier(refName),
+ ),
+ j.identifier("ref"),
+ ),
+ ),
+ ]),
+ ),
+ ),
+ );
+
+ // Replace old ref attribute with new one
+ const newAttributes = path.node.openingElement.attributes.map((attr) =>
+ attr?.name?.name === "ref" ? newRefAttr : attr,
+ );
+ const newOpeningElement = j.jsxOpeningElement(
+ path.node.openingElement.name,
+ newAttributes,
+ path.node.openingElement.selfClosing,
+ );
+ const newElement = j.jsxElement(
+ newOpeningElement,
+ path.node.closingElement,
+ path.node.children,
+ path.node.selfClosing,
+ );
+
+ // Replace old element with new one, preserving comments
+ replaceWithComments(path, newElement);
+ });
+
+ return root.toSource();
+}
diff --git a/codemods/ref-to-arrow-function-ref/tsconfig.json b/codemods/ref-to-arrow-function-ref/tsconfig.json
new file mode 100644
index 0000000000000..817a56caa65f8
--- /dev/null
+++ b/codemods/ref-to-arrow-function-ref/tsconfig.json
@@ -0,0 +1,23 @@
+{
+ "compilerOptions": {
+ "outDir": "./dist",
+ "esModuleInterop": true,
+ "forceConsistentCasingInFileNames": true,
+ "isolatedModules": true,
+ "module": "NodeNext",
+ "skipLibCheck": true,
+ "strict": true,
+ "target": "ES6",
+ "allowJs": true
+ },
+ "include": [
+ "./src/**/*.ts",
+ "./src/**/*.js",
+ "./test/**/*.ts",
+ "./test/**/*.js"
+ ],
+ "exclude": ["node_modules", "./dist/**/*"],
+ "ts-node": {
+ "transpileOnly": true
+ }
+}
diff --git a/codemods/ref-to-arrow-function-ref/vitest.config.ts b/codemods/ref-to-arrow-function-ref/vitest.config.ts
new file mode 100644
index 0000000000000..3f5723c83dbb3
--- /dev/null
+++ b/codemods/ref-to-arrow-function-ref/vitest.config.ts
@@ -0,0 +1,7 @@
+import { configDefaults, defineConfig } from "vitest/config";
+
+export default defineConfig({
+ test: {
+ include: [...configDefaults.include, "**/test/*.ts"],
+ },
+});
diff --git a/codemods/remove-context-provider/.codemodrc.json b/codemods/remove-context-provider/.codemodrc.json
new file mode 100644
index 0000000000000..366144215ae6f
--- /dev/null
+++ b/codemods/remove-context-provider/.codemodrc.json
@@ -0,0 +1,12 @@
+{
+ "version": "1.0.0",
+ "name": "react/19/remove-context-provider",
+ "private": false,
+ "engine": "jscodeshift",
+ "meta": {
+ "tags": ["react", "migration"]
+ },
+ "applicability": {
+ "from": [["react", "<=", "^18.0.0"]]
+ }
+}
diff --git a/codemods/remove-context-provider/README.md b/codemods/remove-context-provider/README.md
new file mode 100644
index 0000000000000..fa1b733d5ffbc
--- /dev/null
+++ b/codemods/remove-context-provider/README.md
@@ -0,0 +1,35 @@
+# Change Context.Provider to Context
+
+## Description
+
+This codemod will remove the usage of `Provider` for contexts; e.g., Context.Provider to Context
+
+## Example
+
+### Before:
+
+```tsx
+function App() {
+ const [theme, setTheme] = useState('light');
+ // ...
+ return (
+
+
+
+ );
+}
+```
+
+### After:
+
+```tsx
+function App() {
+ const [theme, setTheme] = useState('light');
+ // ...
+ return (
+
+
+
+ );
+}
+```
\ No newline at end of file
diff --git a/codemods/remove-context-provider/__testfixtures__/fixture1.input.js b/codemods/remove-context-provider/__testfixtures__/fixture1.input.js
new file mode 100644
index 0000000000000..a066cc86af5e4
--- /dev/null
+++ b/codemods/remove-context-provider/__testfixtures__/fixture1.input.js
@@ -0,0 +1,9 @@
+function App() {
+ const [theme, setTheme] = useState('light');
+
+ return (
+
+
+
+ );
+}
\ No newline at end of file
diff --git a/codemods/remove-context-provider/__testfixtures__/fixture1.output.js b/codemods/remove-context-provider/__testfixtures__/fixture1.output.js
new file mode 100644
index 0000000000000..84f317b83b1bd
--- /dev/null
+++ b/codemods/remove-context-provider/__testfixtures__/fixture1.output.js
@@ -0,0 +1,9 @@
+function App() {
+ const [theme, setTheme] = useState('light');
+
+ return (
+
+
+
+ );
+}
\ No newline at end of file
diff --git a/codemods/remove-context-provider/__testfixtures__/fixture2.input.js b/codemods/remove-context-provider/__testfixtures__/fixture2.input.js
new file mode 100644
index 0000000000000..18cb6e6c52a7b
--- /dev/null
+++ b/codemods/remove-context-provider/__testfixtures__/fixture2.input.js
@@ -0,0 +1,9 @@
+function App() {
+ const [theme, setTheme] = useState('light');
+
+ return (
+
+
+
+ );
+}
\ No newline at end of file
diff --git a/codemods/remove-context-provider/__testfixtures__/fixture2.output.js b/codemods/remove-context-provider/__testfixtures__/fixture2.output.js
new file mode 100644
index 0000000000000..9ef65738c692d
--- /dev/null
+++ b/codemods/remove-context-provider/__testfixtures__/fixture2.output.js
@@ -0,0 +1,9 @@
+function App() {
+ const [theme, setTheme] = useState('light');
+
+ return (
+
+
+
+ );
+}
\ No newline at end of file
diff --git a/codemods/remove-context-provider/__testfixtures__/fixture3.input.js b/codemods/remove-context-provider/__testfixtures__/fixture3.input.js
new file mode 100644
index 0000000000000..9ef65738c692d
--- /dev/null
+++ b/codemods/remove-context-provider/__testfixtures__/fixture3.input.js
@@ -0,0 +1,9 @@
+function App() {
+ const [theme, setTheme] = useState('light');
+
+ return (
+
+
+
+ );
+}
\ No newline at end of file
diff --git a/codemods/remove-context-provider/__testfixtures__/fixture3.output.js b/codemods/remove-context-provider/__testfixtures__/fixture3.output.js
new file mode 100644
index 0000000000000..9ef65738c692d
--- /dev/null
+++ b/codemods/remove-context-provider/__testfixtures__/fixture3.output.js
@@ -0,0 +1,9 @@
+function App() {
+ const [theme, setTheme] = useState('light');
+
+ return (
+
+
+
+ );
+}
\ No newline at end of file
diff --git a/codemods/remove-context-provider/__testfixtures__/fixture4.input.ts b/codemods/remove-context-provider/__testfixtures__/fixture4.input.ts
new file mode 100644
index 0000000000000..978720546270f
--- /dev/null
+++ b/codemods/remove-context-provider/__testfixtures__/fixture4.input.ts
@@ -0,0 +1,9 @@
+function App({ url }: { url: string }) {
+ const [theme, setTheme] = useState<'light' | 'dark'>('light');
+
+ return (
+
+
+
+ );
+}
\ No newline at end of file
diff --git a/codemods/remove-context-provider/__testfixtures__/fixture4.output.ts b/codemods/remove-context-provider/__testfixtures__/fixture4.output.ts
new file mode 100644
index 0000000000000..f1d45694f4883
--- /dev/null
+++ b/codemods/remove-context-provider/__testfixtures__/fixture4.output.ts
@@ -0,0 +1,9 @@
+function App({ url }: { url: string }) {
+ const [theme, setTheme] = useState<'light' | 'dark'>('light');
+
+ return (
+
+
+
+ );
+}
\ No newline at end of file
diff --git a/codemods/remove-context-provider/__testfixtures__/fixture5.input.ts b/codemods/remove-context-provider/__testfixtures__/fixture5.input.ts
new file mode 100644
index 0000000000000..b8d7261041a1b
--- /dev/null
+++ b/codemods/remove-context-provider/__testfixtures__/fixture5.input.ts
@@ -0,0 +1,9 @@
+function App({ url }: { url: string }) {
+ const [theme, setTheme] = useState<'light' | 'dark'>('light');
+
+ return (
+
+
+
+ );
+}
\ No newline at end of file
diff --git a/codemods/remove-context-provider/__testfixtures__/fixture5.output.ts b/codemods/remove-context-provider/__testfixtures__/fixture5.output.ts
new file mode 100644
index 0000000000000..357cc7c77c62c
--- /dev/null
+++ b/codemods/remove-context-provider/__testfixtures__/fixture5.output.ts
@@ -0,0 +1,9 @@
+function App({ url }: { url: string }) {
+ const [theme, setTheme] = useState<'light' | 'dark'>('light');
+
+ return (
+
+
+
+ );
+}
\ No newline at end of file
diff --git a/codemods/remove-context-provider/__testfixtures__/fixture6.input.ts b/codemods/remove-context-provider/__testfixtures__/fixture6.input.ts
new file mode 100644
index 0000000000000..357cc7c77c62c
--- /dev/null
+++ b/codemods/remove-context-provider/__testfixtures__/fixture6.input.ts
@@ -0,0 +1,9 @@
+function App({ url }: { url: string }) {
+ const [theme, setTheme] = useState<'light' | 'dark'>('light');
+
+ return (
+
+
+
+ );
+}
\ No newline at end of file
diff --git a/codemods/remove-context-provider/__testfixtures__/fixture6.output.ts b/codemods/remove-context-provider/__testfixtures__/fixture6.output.ts
new file mode 100644
index 0000000000000..357cc7c77c62c
--- /dev/null
+++ b/codemods/remove-context-provider/__testfixtures__/fixture6.output.ts
@@ -0,0 +1,9 @@
+function App({ url }: { url: string }) {
+ const [theme, setTheme] = useState<'light' | 'dark'>('light');
+
+ return (
+
+
+
+ );
+}
\ No newline at end of file
diff --git a/codemods/remove-context-provider/__tests__/test.ts b/codemods/remove-context-provider/__tests__/test.ts
new file mode 100644
index 0000000000000..9cf9ae9974052
--- /dev/null
+++ b/codemods/remove-context-provider/__tests__/test.ts
@@ -0,0 +1,164 @@
+import assert from 'node:assert/strict';
+import {readFile} from 'node:fs/promises';
+import {join} from 'node:path';
+import jscodeshift, {API, FileInfo} from 'jscodeshift';
+import transform from '../src/index.ts';
+
+export const buildApi = (parser: string | undefined): API => ({
+ j: parser ? jscodeshift.withParser(parser) : jscodeshift,
+ jscodeshift: parser ? jscodeshift.withParser(parser) : jscodeshift,
+ stats: () => {
+ console.error(
+ 'The stats function was called, which is not supported on purpose'
+ );
+ },
+ report: () => {
+ console.error(
+ 'The report function was called, which is not supported on purpose'
+ );
+ },
+});
+
+describe('Context.Provider -> Context', () => {
+ describe('javascript code', () => {
+ it('should replace ThemeContext.Provider with ThemeContext', async () => {
+ const INPUT = await readFile(
+ join(__dirname, '..', '__testfixtures__/fixture1.input.js'),
+ 'utf-8'
+ );
+ const OUTPUT = await readFile(
+ join(__dirname, '..', '__testfixtures__/fixture1.output.js'),
+ 'utf-8'
+ );
+
+ const fileInfo: FileInfo = {
+ path: 'index.ts',
+ source: INPUT,
+ };
+
+ const actualOutput = transform(fileInfo, buildApi('js'));
+
+ assert.deepEqual(
+ actualOutput?.replace(/\W/gm, ''),
+ OUTPUT.replace(/\W/gm, '')
+ );
+ });
+
+ it('should replace Context.Provider with Context', async () => {
+ const INPUT = await readFile(
+ join(__dirname, '..', '__testfixtures__/fixture2.input.js'),
+ 'utf-8'
+ );
+ const OUTPUT = await readFile(
+ join(__dirname, '..', '__testfixtures__/fixture2.output.js'),
+ 'utf-8'
+ );
+
+ const fileInfo: FileInfo = {
+ path: 'index.ts',
+ source: INPUT,
+ };
+
+ const actualOutput = transform(fileInfo, buildApi('js'));
+
+ assert.deepEqual(
+ actualOutput?.replace(/\W/gm, ''),
+ OUTPUT.replace(/\W/gm, '')
+ );
+ });
+
+ it('should do nothing if .Provider does not exist', async () => {
+ const INPUT = await readFile(
+ join(__dirname, '..', '__testfixtures__/fixture3.input.js'),
+ 'utf-8'
+ );
+ const OUTPUT = await readFile(
+ join(__dirname, '..', '__testfixtures__/fixture3.output.js'),
+ 'utf-8'
+ );
+
+ const fileInfo: FileInfo = {
+ path: 'index.ts',
+ source: INPUT,
+ };
+
+ const actualOutput = transform(fileInfo, buildApi('js'));
+
+ assert.deepEqual(
+ actualOutput?.replace(/\W/gm, ''),
+ OUTPUT.replace(/\W/gm, '')
+ );
+ });
+ });
+
+ describe('typescript code', () => {
+ it('should replace ThemeContext.Provider with ThemeContext', async () => {
+ const INPUT = await readFile(
+ join(__dirname, '..', '__testfixtures__/fixture4.input.ts'),
+ 'utf-8'
+ );
+ const OUTPUT = await readFile(
+ join(__dirname, '..', '__testfixtures__/fixture4.output.ts'),
+ 'utf-8'
+ );
+
+ const fileInfo: FileInfo = {
+ path: 'index.ts',
+ source: INPUT,
+ };
+
+ const actualOutput = transform(fileInfo, buildApi('tsx'));
+
+ assert.deepEqual(
+ actualOutput?.replace(/\W/gm, ''),
+ OUTPUT.replace(/\W/gm, '')
+ );
+ });
+
+ it('should replace Context.Provider with Context', async () => {
+ const INPUT = await readFile(
+ join(__dirname, '..', '__testfixtures__/fixture5.input.ts'),
+ 'utf-8'
+ );
+ const OUTPUT = await readFile(
+ join(__dirname, '..', '__testfixtures__/fixture5.output.ts'),
+ 'utf-8'
+ );
+
+ const fileInfo: FileInfo = {
+ path: 'index.ts',
+ source: INPUT,
+ };
+
+ const actualOutput = transform(fileInfo, buildApi('tsx'));
+
+ assert.deepEqual(
+ actualOutput?.replace(/\W/gm, ''),
+ OUTPUT.replace(/\W/gm, '')
+ );
+ });
+
+ it('should do nothing if .Provider does not exist', async () => {
+ const INPUT = await readFile(
+ join(__dirname, '..', '__testfixtures__/fixture6.input.ts'),
+ 'utf-8'
+ );
+ const OUTPUT = await readFile(
+ join(__dirname, '..', '__testfixtures__/fixture6.output.ts'),
+ 'utf-8'
+ );
+
+ const fileInfo: FileInfo = {
+ path: 'index.ts',
+ source: INPUT,
+ };
+
+ const actualOutput = transform(fileInfo, buildApi('tsx'));
+
+ assert.deepEqual(
+ actualOutput?.replace(/\W/gm, ''),
+ OUTPUT.replace(/\W/gm, '')
+ );
+ });
+ });
+});
diff --git a/codemods/remove-context-provider/package.json b/codemods/remove-context-provider/package.json
new file mode 100644
index 0000000000000..7b86ae43f751e
--- /dev/null
+++ b/codemods/remove-context-provider/package.json
@@ -0,0 +1,17 @@
+{
+ "name": "remove-context-provider",
+ "version": "1.0.0",
+ "dependencies": {},
+ "devDependencies": {
+ "@types/node": "^20.12.3",
+ "@types/jscodeshift": "^0.11.10",
+ "jscodeshift": "^0.15.1",
+ "typescript": "^5.2.2"
+ },
+ "files": [
+ "./README.md",
+ "./.codemodrc.json",
+ "./dist/index.cjs"
+ ],
+ "type": "module"
+}
diff --git a/codemods/remove-context-provider/src/index.ts b/codemods/remove-context-provider/src/index.ts
new file mode 100644
index 0000000000000..1ba778878b564
--- /dev/null
+++ b/codemods/remove-context-provider/src/index.ts
@@ -0,0 +1,37 @@
+import type {API, FileInfo} from 'jscodeshift';
+
+export default function transform(
+ file: FileInfo,
+ api: API
+): string | undefined {
+ const j = api.jscodeshift;
+ const root = j(file.source);
+
+ root.findJSXElements().forEach(elementPath => {
+ const {value} = elementPath;
+ const elements = [value.openingElement, value.closingElement];
+ elements.forEach(element => {
+ if (!element) {
+ return;
+ }
+ if (
+ !j.JSXMemberExpression.check(element.name) ||
+ !j.JSXIdentifier.check(element.name.object)
+ ) {
+ return;
+ }
+
+ const objectName = element.name.object.name;
+ const propertyName = element.name.property.name;
+
+ if (
+ objectName.toLocaleLowerCase().includes('context') &&
+ propertyName === 'Provider'
+ ) {
+ element.name = element.name.object;
+ }
+ });
+ });
+
+ return root.toSource();
+}
diff --git a/codemods/remove-context-provider/tsconfig.json b/codemods/remove-context-provider/tsconfig.json
new file mode 100644
index 0000000000000..5717e5c332cf3
--- /dev/null
+++ b/codemods/remove-context-provider/tsconfig.json
@@ -0,0 +1,25 @@
+{
+ "compilerOptions": {
+ "outDir": "./dist",
+ "esModuleInterop": true,
+ "forceConsistentCasingInFileNames": true,
+ "isolatedModules": true,
+ "module": "NodeNext",
+ "skipLibCheck": true,
+ "strict": true,
+ "target": "ES6",
+ "allowJs": true,
+ "allowImportingTsExtensions": true,
+ "noEmit": true
+ },
+ "include": [
+ "./src/**/*.ts",
+ "./src/**/*.js",
+ "./__tests__/**/*.ts",
+ "./__tests__/**/*.js"
+ ],
+ "exclude": ["node_modules", "./dist/**/*"],
+ "ts-node": {
+ "transpileOnly": true
+ }
+}
\ No newline at end of file
diff --git a/codemods/remove-forward-ref/.codemodrc.json b/codemods/remove-forward-ref/.codemodrc.json
new file mode 100644
index 0000000000000..e4b8bcea814a0
--- /dev/null
+++ b/codemods/remove-forward-ref/.codemodrc.json
@@ -0,0 +1,13 @@
+{
+ "version": "1.0.0",
+ "private": false,
+ "name": "react/19/remove-forward-ref",
+ "description": "Codemod to remove React.forwardRef function that will be deprecated in next major React release",
+ "engine": "jscodeshift",
+ "meta": {
+ "tags": ["react", "migration"]
+ },
+ "applicability": {
+ "from": [["react", "<=", "^18.0.0"]]
+ }
+}
diff --git a/codemods/remove-forward-ref/README.md b/codemods/remove-forward-ref/README.md
new file mode 100644
index 0000000000000..484713f7a1642
--- /dev/null
+++ b/codemods/remove-forward-ref/README.md
@@ -0,0 +1,25 @@
+# Replace forwardRef with ref prop
+
+## Description
+
+React.forwardRef will be deprecated for Function Components in near future. This codemod removes forwardRef function.
+
+## Example
+
+### Before:
+
+```jsx
+import { forwardRef } from 'react';
+
+const MyInput = forwardRef(function MyInput(props, ref) {
+ // ...
+});
+```
+
+### After:
+
+```tsx
+const MyInput = function MyInput({ ref, ...otherProps }) {
+ // ...
+};
+```
diff --git a/codemods/remove-forward-ref/__testfixtures__/fixture1.input.js b/codemods/remove-forward-ref/__testfixtures__/fixture1.input.js
new file mode 100644
index 0000000000000..faf9c4c943827
--- /dev/null
+++ b/codemods/remove-forward-ref/__testfixtures__/fixture1.input.js
@@ -0,0 +1,5 @@
+import { forwardRef } from 'react';
+
+const MyInput = forwardRef((props, ref) => {
+ return null;
+});
\ No newline at end of file
diff --git a/codemods/remove-forward-ref/__testfixtures__/fixture1.output.js b/codemods/remove-forward-ref/__testfixtures__/fixture1.output.js
new file mode 100644
index 0000000000000..30d50bcba73a0
--- /dev/null
+++ b/codemods/remove-forward-ref/__testfixtures__/fixture1.output.js
@@ -0,0 +1,4 @@
+const MyInput = props => {
+ const { ref } = props;
+ return null;
+};
\ No newline at end of file
diff --git a/codemods/remove-forward-ref/__testfixtures__/fixture2.input.js b/codemods/remove-forward-ref/__testfixtures__/fixture2.input.js
new file mode 100644
index 0000000000000..ee8f84e0c2d6a
--- /dev/null
+++ b/codemods/remove-forward-ref/__testfixtures__/fixture2.input.js
@@ -0,0 +1,5 @@
+import { forwardRef } from 'react';
+
+const MyInput = forwardRef(function A(props, ref) {
+ return null;
+});
\ No newline at end of file
diff --git a/codemods/remove-forward-ref/__testfixtures__/fixture2.output.js b/codemods/remove-forward-ref/__testfixtures__/fixture2.output.js
new file mode 100644
index 0000000000000..f60690b34366b
--- /dev/null
+++ b/codemods/remove-forward-ref/__testfixtures__/fixture2.output.js
@@ -0,0 +1,4 @@
+const MyInput = function A(props) {
+ const { ref } = props;
+ return null;
+};
\ No newline at end of file
diff --git a/codemods/remove-forward-ref/__testfixtures__/fixture3.input.js b/codemods/remove-forward-ref/__testfixtures__/fixture3.input.js
new file mode 100644
index 0000000000000..aa2f3cc8bd16d
--- /dev/null
+++ b/codemods/remove-forward-ref/__testfixtures__/fixture3.input.js
@@ -0,0 +1,5 @@
+import { forwardRef } from 'react';
+
+const MyInput = forwardRef(function MyInput(props, ref) {
+ return null;
+});
\ No newline at end of file
diff --git a/codemods/remove-forward-ref/__testfixtures__/fixture3.output.js b/codemods/remove-forward-ref/__testfixtures__/fixture3.output.js
new file mode 100644
index 0000000000000..20c8ac77ad6ca
--- /dev/null
+++ b/codemods/remove-forward-ref/__testfixtures__/fixture3.output.js
@@ -0,0 +1,4 @@
+const MyInput = function MyInput(props) {
+ const { ref } = props;
+ return null;
+};
\ No newline at end of file
diff --git a/codemods/remove-forward-ref/__testfixtures__/fixture4.input.ts b/codemods/remove-forward-ref/__testfixtures__/fixture4.input.ts
new file mode 100644
index 0000000000000..7e4b01ae19ba6
--- /dev/null
+++ b/codemods/remove-forward-ref/__testfixtures__/fixture4.input.ts
@@ -0,0 +1,6 @@
+import type { X } from "react";
+import { forwardRef, type Y } from 'react';
+
+const MyInput = forwardRef(function MyInput(props, ref) {
+ return null;
+});
\ No newline at end of file
diff --git a/codemods/remove-forward-ref/__testfixtures__/fixture4.output.ts b/codemods/remove-forward-ref/__testfixtures__/fixture4.output.ts
new file mode 100644
index 0000000000000..e39ce2bef8cc1
--- /dev/null
+++ b/codemods/remove-forward-ref/__testfixtures__/fixture4.output.ts
@@ -0,0 +1,6 @@
+import type { X } from "react";
+import { type Y } from 'react';
+const MyInput = function MyInput(props) {
+ const { ref } = props;
+ return null;
+};
\ No newline at end of file
diff --git a/codemods/remove-forward-ref/__testfixtures__/fixture5.input.js b/codemods/remove-forward-ref/__testfixtures__/fixture5.input.js
new file mode 100644
index 0000000000000..e6675dc46725d
--- /dev/null
+++ b/codemods/remove-forward-ref/__testfixtures__/fixture5.input.js
@@ -0,0 +1,5 @@
+import { forwardRef, useState } from 'react';
+
+const MyInput = forwardRef(function MyInput(props, ref) {
+ return null;
+});
\ No newline at end of file
diff --git a/codemods/remove-forward-ref/__testfixtures__/fixture5.output.js b/codemods/remove-forward-ref/__testfixtures__/fixture5.output.js
new file mode 100644
index 0000000000000..aa1f8a5eac609
--- /dev/null
+++ b/codemods/remove-forward-ref/__testfixtures__/fixture5.output.js
@@ -0,0 +1,6 @@
+import { useState } from 'react';
+
+const MyInput = function MyInput(props) {
+ const { ref } = props;
+ return null;
+};
\ No newline at end of file
diff --git a/codemods/remove-forward-ref/__testfixtures__/fixture6.input.js b/codemods/remove-forward-ref/__testfixtures__/fixture6.input.js
new file mode 100644
index 0000000000000..b7bb0d7f78d9f
--- /dev/null
+++ b/codemods/remove-forward-ref/__testfixtures__/fixture6.input.js
@@ -0,0 +1,5 @@
+import { forwardRef } from 'react';
+
+const MyInput = forwardRef(function MyInput({ onChange }, ref) {
+ return
+});
\ No newline at end of file
diff --git a/codemods/remove-forward-ref/__testfixtures__/fixture6.output.js b/codemods/remove-forward-ref/__testfixtures__/fixture6.output.js
new file mode 100644
index 0000000000000..78f5ef78e9440
--- /dev/null
+++ b/codemods/remove-forward-ref/__testfixtures__/fixture6.output.js
@@ -0,0 +1,3 @@
+const MyInput = function MyInput({ ref, onChange }) {
+ return
+};
\ No newline at end of file
diff --git a/codemods/remove-forward-ref/__testfixtures__/fixture7.input.js b/codemods/remove-forward-ref/__testfixtures__/fixture7.input.js
new file mode 100644
index 0000000000000..2d66977de9861
--- /dev/null
+++ b/codemods/remove-forward-ref/__testfixtures__/fixture7.input.js
@@ -0,0 +1,5 @@
+import { forwardRef } from 'react';
+
+const MyInput = forwardRef(function MyInput(props, ref) {
+ return
+});
\ No newline at end of file
diff --git a/codemods/remove-forward-ref/__testfixtures__/fixture7.output.js b/codemods/remove-forward-ref/__testfixtures__/fixture7.output.js
new file mode 100644
index 0000000000000..13b6383fcf9da
--- /dev/null
+++ b/codemods/remove-forward-ref/__testfixtures__/fixture7.output.js
@@ -0,0 +1,4 @@
+const MyInput = function MyInput(props) {
+ const { ref } = props;
+ return
+};
\ No newline at end of file
diff --git a/codemods/remove-forward-ref/__testfixtures__/fixture8.input.ts b/codemods/remove-forward-ref/__testfixtures__/fixture8.input.ts
new file mode 100644
index 0000000000000..606f1f83e5822
--- /dev/null
+++ b/codemods/remove-forward-ref/__testfixtures__/fixture8.input.ts
@@ -0,0 +1,6 @@
+import { forwardRef } from 'react';
+type Props = { a: 1 };
+
+const MyInput = forwardRef
((props, ref) => {
+ return null;
+});
\ No newline at end of file
diff --git a/codemods/remove-forward-ref/__testfixtures__/fixture8.output.ts b/codemods/remove-forward-ref/__testfixtures__/fixture8.output.ts
new file mode 100644
index 0000000000000..3f9eafba0f7fe
--- /dev/null
+++ b/codemods/remove-forward-ref/__testfixtures__/fixture8.output.ts
@@ -0,0 +1,5 @@
+type Props = { a: 1 };
+const MyInput = (props: Props & { ref: React.RefObject; }) => {
+ const { ref } = props;
+ return null;
+};
\ No newline at end of file
diff --git a/codemods/remove-forward-ref/__testfixtures__/fixture9.input.ts b/codemods/remove-forward-ref/__testfixtures__/fixture9.input.ts
new file mode 100644
index 0000000000000..cae62026a9327
--- /dev/null
+++ b/codemods/remove-forward-ref/__testfixtures__/fixture9.input.ts
@@ -0,0 +1,8 @@
+import { forwardRef } from 'react';
+
+const MyComponent = forwardRef(function Component(
+ myProps: Props,
+ myRef: React.ForwardedRef
+ ) {
+ return null;
+});
\ No newline at end of file
diff --git a/codemods/remove-forward-ref/__testfixtures__/fixture9.output.ts b/codemods/remove-forward-ref/__testfixtures__/fixture9.output.ts
new file mode 100644
index 0000000000000..78e73af32c45e
--- /dev/null
+++ b/codemods/remove-forward-ref/__testfixtures__/fixture9.output.ts
@@ -0,0 +1,6 @@
+const MyComponent = function Component(
+ myProps: Props & { ref: React.RefObject; }
+) {
+ const { ref: myRef } = myProps;
+ return null;
+};
\ No newline at end of file
diff --git a/codemods/remove-forward-ref/__tests__/test.ts b/codemods/remove-forward-ref/__tests__/test.ts
new file mode 100644
index 0000000000000..0ebc3484fe925
--- /dev/null
+++ b/codemods/remove-forward-ref/__tests__/test.ts
@@ -0,0 +1,227 @@
+import assert from 'node:assert/strict';
+import {readFile} from 'node:fs/promises';
+import {join} from 'node:path';
+import jscodeshift, {API, FileInfo} from 'jscodeshift';
+import transform from '../src/index.ts';
+
+export const buildApi = (parser: string | undefined): API => ({
+ j: parser ? jscodeshift.withParser(parser) : jscodeshift,
+ jscodeshift: parser ? jscodeshift.withParser(parser) : jscodeshift,
+ stats: () => {
+ console.error(
+ 'The stats function was called, which is not supported on purpose'
+ );
+ },
+ report: () => {
+ console.error(
+ 'The report function was called, which is not supported on purpose'
+ );
+ },
+});
+
+describe('react/remove-forward-ref', () => {
+ it('Unwraps the render function: render function is ArrowFunctionExpression', async () => {
+ const INPUT = await readFile(
+ join(__dirname, '..', '__testfixtures__/fixture1.input.js'),
+ 'utf-8'
+ );
+ const OUTPUT = await readFile(
+ join(__dirname, '..', '__testfixtures__/fixture1.output.js'),
+ 'utf-8'
+ );
+
+ const fileInfo: FileInfo = {
+ path: 'index.js',
+ source: INPUT,
+ };
+
+ const actualOutput = transform(fileInfo, buildApi('tsx'));
+
+ assert.deepEqual(
+ actualOutput?.replace(/\s/gm, ''),
+ OUTPUT.replace(/\s/gm, '')
+ );
+ });
+
+ it('Unwraps the render function: render function is FunctionExpression', async () => {
+ const INPUT = await readFile(
+ join(__dirname, '..', '__testfixtures__/fixture2.input.js'),
+ 'utf-8'
+ );
+ const OUTPUT = await readFile(
+ join(__dirname, '..', '__testfixtures__/fixture2.output.js'),
+ 'utf-8'
+ );
+
+ const fileInfo: FileInfo = {
+ path: 'index.js',
+ source: INPUT,
+ };
+
+ const actualOutput = transform(fileInfo, buildApi('tsx'));
+
+ assert.deepEqual(
+ actualOutput?.replace(/\s/gm, ''),
+ OUTPUT.replace(/\s/gm, '')
+ );
+ });
+
+ it('forwardRef import: removes the import when only forwardRef is a single specifier', async () => {
+ const INPUT = await readFile(
+ join(__dirname, '..', '__testfixtures__/fixture3.input.js'),
+ 'utf-8'
+ );
+ const OUTPUT = await readFile(
+ join(__dirname, '..', '__testfixtures__/fixture3.output.js'),
+ 'utf-8'
+ );
+
+ const fileInfo: FileInfo = {
+ path: 'index.js',
+ source: INPUT,
+ };
+
+ const actualOutput = transform(fileInfo, buildApi('tsx'));
+
+ assert.deepEqual(
+ actualOutput?.replace(/\s/gm, ''),
+ OUTPUT.replace(/\s/gm, '')
+ );
+ });
+
+ it('forwardRef import: should not remove type imports', async () => {
+ const INPUT = await readFile(
+ join(__dirname, '..', '__testfixtures__/fixture4.input.ts'),
+ 'utf-8'
+ );
+ const OUTPUT = await readFile(
+ join(__dirname, '..', '__testfixtures__/fixture4.output.ts'),
+ 'utf-8'
+ );
+
+ const fileInfo: FileInfo = {
+ path: 'index.js',
+ source: INPUT,
+ };
+
+ const actualOutput = transform(fileInfo, buildApi('tsx'));
+
+ assert.deepEqual(
+ actualOutput?.replace(/\s/gm, ''),
+ OUTPUT.replace(/\s/gm, '')
+ );
+ });
+
+ it('forwardRef import: removes forwardRef specifier', async () => {
+ const INPUT = await readFile(
+ join(__dirname, '..', '__testfixtures__/fixture5.input.js'),
+ 'utf-8'
+ );
+ const OUTPUT = await readFile(
+ join(__dirname, '..', '__testfixtures__/fixture5.output.js'),
+ 'utf-8'
+ );
+
+ const fileInfo: FileInfo = {
+ path: 'index.js',
+ source: INPUT,
+ };
+
+ const actualOutput = transform(fileInfo, buildApi('tsx'));
+
+ assert.deepEqual(
+ actualOutput?.replace(/\s/gm, ''),
+ OUTPUT.replace(/\s/gm, '')
+ );
+ });
+
+ it('Replaces the second arg of the render function: props are ObjectPattern', async () => {
+ const INPUT = await readFile(
+ join(__dirname, '..', '__testfixtures__/fixture6.input.js'),
+ 'utf-8'
+ );
+ const OUTPUT = await readFile(
+ join(__dirname, '..', '__testfixtures__/fixture6.output.js'),
+ 'utf-8'
+ );
+
+ const fileInfo: FileInfo = {
+ path: 'index.js',
+ source: INPUT,
+ };
+
+ const actualOutput = transform(fileInfo, buildApi('tsx'));
+
+ assert.deepEqual(
+ actualOutput?.replace(/\s/gm, ''),
+ OUTPUT.replace(/\s/gm, '')
+ );
+ });
+
+ it('Replaces the second arg of the render function: props are Identifier', async () => {
+ const INPUT = await readFile(
+ join(__dirname, '..', '__testfixtures__/fixture7.input.js'),
+ 'utf-8'
+ );
+ const OUTPUT = await readFile(
+ join(__dirname, '..', '__testfixtures__/fixture7.output.js'),
+ 'utf-8'
+ );
+
+ const fileInfo: FileInfo = {
+ path: 'index.js',
+ source: INPUT,
+ };
+
+ const actualOutput = transform(fileInfo, buildApi('tsx'));
+
+ assert.deepEqual(
+ actualOutput?.replace(/\s/gm, ''),
+ OUTPUT.replace(/\s/gm, '')
+ );
+ });
+
+ it('Typescript: reuses forwardRef typeArguments', async () => {
+ const INPUT = await readFile(
+ join(__dirname, '..', '__testfixtures__/fixture8.input.ts'),
+ 'utf-8'
+ );
+ const OUTPUT = await readFile(
+ join(__dirname, '..', '__testfixtures__/fixture8.output.ts'),
+ 'utf-8'
+ );
+
+ const fileInfo: FileInfo = {
+ path: 'index.js',
+ source: INPUT,
+ };
+
+ const actualOutput = transform(fileInfo, buildApi('tsx'));
+ assert.deepEqual(
+ actualOutput?.replace(/\s/gm, ''),
+ OUTPUT.replace(/\s/gm, '')
+ );
+ });
+
+ it('Typescript: reuses wrapped function type arguments', async () => {
+ const INPUT = await readFile(
+ join(__dirname, '..', '__testfixtures__/fixture9.input.ts'),
+ 'utf-8'
+ );
+ const OUTPUT = await readFile(
+ join(__dirname, '..', '__testfixtures__/fixture9.output.ts'),
+ 'utf-8'
+ );
+
+ const fileInfo: FileInfo = {
+ path: 'index.js',
+ source: INPUT,
+ };
+
+ const actualOutput = transform(fileInfo, buildApi('tsx'));
+ assert.deepEqual(
+ actualOutput?.replace(/\s/gm, ''),
+ OUTPUT.replace(/\s/gm, '')
+ );
+ });
+});
diff --git a/codemods/remove-forward-ref/package.json b/codemods/remove-forward-ref/package.json
new file mode 100644
index 0000000000000..a8c8cfe86c0ff
--- /dev/null
+++ b/codemods/remove-forward-ref/package.json
@@ -0,0 +1,17 @@
+{
+ "name": "remove-forward-ref",
+ "version": "1.0.0",
+ "dependencies": {},
+ "devDependencies": {
+ "@types/node": "^20.12.3",
+ "@types/jscodeshift": "^0.11.10",
+ "jscodeshift": "^0.15.1",
+ "typescript": "^5.2.2"
+ },
+ "files": [
+ "./README.md",
+ "./.codemodrc.json",
+ "./dist/index.cjs"
+ ],
+ "type": "module"
+}
diff --git a/codemods/remove-forward-ref/src/index.ts b/codemods/remove-forward-ref/src/index.ts
new file mode 100644
index 0000000000000..94ea9ee1b2458
--- /dev/null
+++ b/codemods/remove-forward-ref/src/index.ts
@@ -0,0 +1,248 @@
+import type {
+ API,
+ BlockStatement,
+ FileInfo,
+ Identifier,
+ JSCodeshift,
+ TSTypeReference,
+ Transform,
+} from "jscodeshift";
+
+// Props & { ref: React.RefObject[}
+const buildPropsAndRefIntersectionTypeAnnotation = (
+ j: JSCodeshift,
+ propType: TSTypeReference,
+ refType: TSTypeReference,
+) =>
+ j.tsTypeAnnotation(
+ j.tsIntersectionType([
+ propType,
+ j.tsTypeLiteral([
+ j.tsPropertySignature.from({
+ key: j.identifier("ref"),
+ typeAnnotation: j.tsTypeAnnotation(
+ j.tsTypeReference.from({
+ typeName: j.tsQualifiedName(
+ j.identifier("React"),
+ j.identifier("RefObject"),
+ ),
+ typeParameters: j.tsTypeParameterInstantiation([refType]),
+ }),
+ ),
+ }),
+ ]),
+ ]),
+ );
+
+// const { ref } = props;
+const buildRefArgVariableDeclaration = (
+ j: JSCodeshift,
+ refArgName: string,
+ propArgName: string,
+) =>
+ j.variableDeclaration("const", [
+ j.variableDeclarator(
+ j.objectPattern([
+ j.objectProperty.from({
+ shorthand: true,
+ key: j.identifier("ref"),
+ value: j.identifier(refArgName),
+ }),
+ ]),
+ j.identifier(propArgName),
+ ),
+ ]);
+
+// React.ForwardedRef => HTMLButtonElement
+const getRefTypeFromRefArg = (j: JSCodeshift, refArg: Identifier) => {
+ const typeReference = refArg.typeAnnotation?.typeAnnotation;
+
+ if (
+ !j.TSTypeReference.check(typeReference) ||
+ !j.TSQualifiedName.check(typeReference.typeName)
+ ) {
+ return null;
+ }
+
+ const { right } = typeReference.typeName;
+
+ if (!j.Identifier.check(right) || right.name === "forwardedRef") {
+ return null;
+ }
+
+ const [firstTypeParameter] = typeReference.typeParameters?.params ?? [];
+
+ if (!j.TSTypeReference.check(firstTypeParameter)) {
+ return null;
+ }
+
+ return firstTypeParameter;
+};
+
+export default function transform(file: FileInfo, api: API) {
+ const { j } = api;
+
+ const root = j(file.source);
+
+ let dirtyFlag = false;
+
+ root
+ .find(j.CallExpression, {
+ callee: {
+ type: "Identifier",
+ name: "forwardRef",
+ },
+ })
+ .replaceWith((callExpressionPath) => {
+ const [renderFunctionArg] = callExpressionPath.node.arguments;
+
+ if (
+ !j.FunctionExpression.check(renderFunctionArg) &&
+ !j.ArrowFunctionExpression.check(renderFunctionArg)
+ ) {
+ return null;
+ }
+
+ const [propsArg, refArg] = renderFunctionArg.params;
+
+ if (!j.Identifier.check(refArg) || propsArg === undefined) {
+ return null;
+ }
+
+ const refArgTypeReference = getRefTypeFromRefArg(j, refArg);
+
+ // remove refArg
+ renderFunctionArg.params.splice(1, 1);
+
+ const refArgName = refArg.name;
+
+ // if props are ObjectPattern, push ref as ObjectProperty
+ if (j.ObjectPattern.check(propsArg)) {
+ propsArg.properties.unshift(
+ j.objectProperty.from({
+ shorthand: true,
+ key: j.identifier(refArgName),
+ value: j.identifier(refArgName),
+ }),
+ );
+
+ // update prop arg type
+ const propsArgTypeReference = propsArg.typeAnnotation?.typeAnnotation;
+
+ if (
+ j.TSTypeReference.check(propsArgTypeReference) &&
+ j.TSTypeReference.check(refArgTypeReference)
+ ) {
+ propsArg.typeAnnotation = buildPropsAndRefIntersectionTypeAnnotation(
+ j,
+ propsArgTypeReference,
+ refArgTypeReference,
+ );
+ }
+ }
+
+ // if props arg is Identifier, push ref variable declaration to the function body
+ if (j.Identifier.check(propsArg)) {
+ // if we have arrow function with implicit return, we want to wrap it with BlockStatement
+ if (
+ j.ArrowFunctionExpression.check(renderFunctionArg) &&
+ !j.BlockStatement.check(renderFunctionArg.body)
+ ) {
+ renderFunctionArg.body = j.blockStatement.from({
+ body: [j.returnStatement(renderFunctionArg.body)],
+ });
+ }
+
+ const newDeclaration = buildRefArgVariableDeclaration(
+ j,
+ refArg.name,
+ propsArg.name,
+ );
+
+ (renderFunctionArg.body as BlockStatement).body.unshift(newDeclaration);
+
+ const propsArgTypeReference = propsArg.typeAnnotation?.typeAnnotation;
+
+ if (
+ j.TSTypeReference.check(propsArgTypeReference) &&
+ j.TSTypeReference.check(refArgTypeReference)
+ ) {
+ propsArg.typeAnnotation = buildPropsAndRefIntersectionTypeAnnotation(
+ j,
+ propsArgTypeReference,
+ refArgTypeReference,
+ );
+ }
+ }
+
+ /**
+ * Transform ts types: forwardRef type arguments are used
+ */
+
+ // @ts-expect-error Property 'typeParameters' does not exist on type 'CallExpression'.
+ const typeParameters = callExpressionPath.node.typeParameters;
+
+ // if typeParameters are used in forwardRef generic, reuse them to annotate props type
+ // forwardRef][((props) => { ... }) ====> (props: Props & { ref: React.RefObject][ }) => { ... }
+ if (
+ j.TSTypeParameterInstantiation.check(typeParameters) &&
+ propsArg !== undefined &&
+ "typeAnnotation" in propsArg
+ ) {
+ const [refType, propType] = typeParameters.params;
+
+ if (
+ j.TSTypeReference.check(refType) &&
+ j.TSTypeReference.check(propType)
+ ) {
+ propsArg.typeAnnotation = buildPropsAndRefIntersectionTypeAnnotation(
+ j,
+ propType,
+ refType,
+ );
+ }
+ }
+
+ dirtyFlag = true;
+ return renderFunctionArg;
+ });
+
+ /**
+ * handle import
+ */
+ if (dirtyFlag) {
+ root
+ .find(j.ImportDeclaration, {
+ source: {
+ value: "react",
+ },
+ })
+ .forEach((importDeclarationPath) => {
+ const { specifiers, importKind } = importDeclarationPath.node;
+
+ if (importKind !== "value") {
+ return;
+ }
+
+ if (specifiers === undefined) {
+ return;
+ }
+
+ const forwardRefImportSpecifierIndex = specifiers.findIndex(
+ (s) => j.ImportSpecifier.check(s) && s.imported.name === "forwardRef",
+ );
+
+ specifiers.splice(forwardRefImportSpecifierIndex, 1);
+ })
+ .filter((importDeclarationPath) => {
+ const { specifiers } = importDeclarationPath.node;
+ // remove the import if there are no more specifiers left after removing forwardRef
+ return specifiers === undefined || specifiers.length === 0;
+ })
+ .remove();
+ }
+
+ return root.toSource();
+}
+
+transform satisfies Transform;
diff --git a/codemods/remove-forward-ref/tsconfig.json b/codemods/remove-forward-ref/tsconfig.json
new file mode 100644
index 0000000000000..5717e5c332cf3
--- /dev/null
+++ b/codemods/remove-forward-ref/tsconfig.json
@@ -0,0 +1,25 @@
+{
+ "compilerOptions": {
+ "outDir": "./dist",
+ "esModuleInterop": true,
+ "forceConsistentCasingInFileNames": true,
+ "isolatedModules": true,
+ "module": "NodeNext",
+ "skipLibCheck": true,
+ "strict": true,
+ "target": "ES6",
+ "allowJs": true,
+ "allowImportingTsExtensions": true,
+ "noEmit": true
+ },
+ "include": [
+ "./src/**/*.ts",
+ "./src/**/*.js",
+ "./__tests__/**/*.ts",
+ "./__tests__/**/*.js"
+ ],
+ "exclude": ["node_modules", "./dist/**/*"],
+ "ts-node": {
+ "transpileOnly": true
+ }
+}
\ No newline at end of file
diff --git a/codemods/remove-memoization-hooks/.codemodrc.json b/codemods/remove-memoization-hooks/.codemodrc.json
new file mode 100644
index 0000000000000..2071e7ccf60eb
--- /dev/null
+++ b/codemods/remove-memoization-hooks/.codemodrc.json
@@ -0,0 +1,12 @@
+{
+ "version": "1.0.0",
+ "name": "react/19/remove-memoization",
+ "private": false,
+ "engine": "jscodeshift",
+ "meta": {
+ "tags": ["react", "migration"]
+ },
+ "applicability": {
+ "from": [["react", "<=", "18"]]
+ }
+}
diff --git a/codemods/remove-memoization-hooks/README.md b/codemods/remove-memoization-hooks/README.md
new file mode 100644
index 0000000000000..c505093e2ec8f
--- /dev/null
+++ b/codemods/remove-memoization-hooks/README.md
@@ -0,0 +1,5 @@
+# Remove Manual Memoization Hooks
+
+## Description
+
+This codemod will remove manual memoization hooks: `useCallback`, `useMemo` and `memo`. This codemod goes hand in hand with React Compiler.
diff --git a/codemods/remove-memoization-hooks/__testfixtures__/fixture1.input.js b/codemods/remove-memoization-hooks/__testfixtures__/fixture1.input.js
new file mode 100644
index 0000000000000..b85ec0590e28a
--- /dev/null
+++ b/codemods/remove-memoization-hooks/__testfixtures__/fixture1.input.js
@@ -0,0 +1,8 @@
+import { useCallback } from 'react';
+
+function Component() {
+ const selectedDateMin3DaysDifference = useCallback(() => {
+ const diff = today.diff(selectedDate, "days");
+ return diff > 3 || diff < -3;
+ }, [today, selectedDate]);
+}
\ No newline at end of file
diff --git a/codemods/remove-memoization-hooks/__testfixtures__/fixture1.output.js b/codemods/remove-memoization-hooks/__testfixtures__/fixture1.output.js
new file mode 100644
index 0000000000000..494a423856305
--- /dev/null
+++ b/codemods/remove-memoization-hooks/__testfixtures__/fixture1.output.js
@@ -0,0 +1,6 @@
+function Component() {
+ const selectedDateMin3DaysDifference = () => {
+ const diff = today.diff(selectedDate, "days");
+ return diff > 3 || diff < -3;
+ };
+}
\ No newline at end of file
diff --git a/codemods/remove-memoization-hooks/__testfixtures__/fixture2.input.js b/codemods/remove-memoization-hooks/__testfixtures__/fixture2.input.js
new file mode 100644
index 0000000000000..8a9f3e5a3b448
--- /dev/null
+++ b/codemods/remove-memoization-hooks/__testfixtures__/fixture2.input.js
@@ -0,0 +1,8 @@
+import { useMemo } from 'react';
+
+function Component() {
+ const selectedDateMin3DaysDifference = useMemo(() => {
+ const diff = today.diff(selectedDate, "days");
+ return diff > 3 || diff < -3;
+ }, [today, selectedDate]);
+}
\ No newline at end of file
diff --git a/codemods/remove-memoization-hooks/__testfixtures__/fixture2.output.js b/codemods/remove-memoization-hooks/__testfixtures__/fixture2.output.js
new file mode 100644
index 0000000000000..494a423856305
--- /dev/null
+++ b/codemods/remove-memoization-hooks/__testfixtures__/fixture2.output.js
@@ -0,0 +1,6 @@
+function Component() {
+ const selectedDateMin3DaysDifference = () => {
+ const diff = today.diff(selectedDate, "days");
+ return diff > 3 || diff < -3;
+ };
+}
\ No newline at end of file
diff --git a/codemods/remove-memoization-hooks/__testfixtures__/fixture3.input.js b/codemods/remove-memoization-hooks/__testfixtures__/fixture3.input.js
new file mode 100644
index 0000000000000..a2809f30eea27
--- /dev/null
+++ b/codemods/remove-memoization-hooks/__testfixtures__/fixture3.input.js
@@ -0,0 +1,7 @@
+import { memo } from 'react';
+
+const MyComponent = ({ name }) => {
+ return ]Hello, {name}!
;
+};
+
+const MemoizedMyComponent = memo(MyComponent);
\ No newline at end of file
diff --git a/codemods/remove-memoization-hooks/__testfixtures__/fixture3.output.js b/codemods/remove-memoization-hooks/__testfixtures__/fixture3.output.js
new file mode 100644
index 0000000000000..6e0f9111d76ee
--- /dev/null
+++ b/codemods/remove-memoization-hooks/__testfixtures__/fixture3.output.js
@@ -0,0 +1,5 @@
+const MyComponent = ({ name }) => {
+ return Hello, {name}!
;
+};
+
+const MemoizedMyComponent = MyComponent;
\ No newline at end of file
diff --git a/codemods/remove-memoization-hooks/__testfixtures__/fixture4.input.js b/codemods/remove-memoization-hooks/__testfixtures__/fixture4.input.js
new file mode 100644
index 0000000000000..a175d1883057a
--- /dev/null
+++ b/codemods/remove-memoization-hooks/__testfixtures__/fixture4.input.js
@@ -0,0 +1,21 @@
+import React from 'react';
+
+function Component() {
+ const state = React.useState();
+
+ const example1 = React.useMemo(() => {
+ const diff = today.diff(selectedDate, "days");
+ return diff > 3 || diff < -3;
+ }, [today, selectedDate]);
+
+ const example2 = React.useCallback(() => {
+ const diff = today.diff(selectedDate, "days");
+ return diff > 3 || diff < -3;
+ }, [today, selectedDate]);
+}
+
+const MyComponent = ({ name }) => {
+ return Hello, {name}!
;
+};
+
+const MemoizedMyComponent = React.memo(MyComponent);
\ No newline at end of file
diff --git a/codemods/remove-memoization-hooks/__testfixtures__/fixture4.output.js b/codemods/remove-memoization-hooks/__testfixtures__/fixture4.output.js
new file mode 100644
index 0000000000000..aa4e313a53c7e
--- /dev/null
+++ b/codemods/remove-memoization-hooks/__testfixtures__/fixture4.output.js
@@ -0,0 +1,21 @@
+import React from 'react';
+
+function Component() {
+ const state = React.useState();
+
+ const example1 = () => {
+ const diff = today.diff(selectedDate, "days");
+ return diff > 3 || diff < -3;
+ };
+
+ const example2 = () => {
+ const diff = today.diff(selectedDate, "days");
+ return diff > 3 || diff < -3;
+ };
+}
+
+const MyComponent = ({ name }) => {
+ return Hello, {name}!
;
+};
+
+const MemoizedMyComponent = MyComponent;
diff --git a/codemods/remove-memoization-hooks/__testfixtures__/fixture5.input.ts b/codemods/remove-memoization-hooks/__testfixtures__/fixture5.input.ts
new file mode 100644
index 0000000000000..63437cccb6c29
--- /dev/null
+++ b/codemods/remove-memoization-hooks/__testfixtures__/fixture5.input.ts
@@ -0,0 +1,8 @@
+import { useCallback } from 'react';
+
+function Component({ url }: { url: string }) {
+ const selectedDateMin3DaysDifference = useCallback(() => {
+ const diff = today.diff(selectedDate, "days");
+ return diff > 3 || diff < -3;
+ }, [today, selectedDate]);
+}
\ No newline at end of file
diff --git a/codemods/remove-memoization-hooks/__testfixtures__/fixture5.output.ts b/codemods/remove-memoization-hooks/__testfixtures__/fixture5.output.ts
new file mode 100644
index 0000000000000..9ed5b47588a9a
--- /dev/null
+++ b/codemods/remove-memoization-hooks/__testfixtures__/fixture5.output.ts
@@ -0,0 +1,6 @@
+function Component({ url }: { url: string }) {
+ const selectedDateMin3DaysDifference = () => {
+ const diff = today.diff(selectedDate, "days");
+ return diff > 3 || diff < -3;
+ };
+}
\ No newline at end of file
diff --git a/codemods/remove-memoization-hooks/__testfixtures__/fixture6.input.ts b/codemods/remove-memoization-hooks/__testfixtures__/fixture6.input.ts
new file mode 100644
index 0000000000000..522b68bd9541c
--- /dev/null
+++ b/codemods/remove-memoization-hooks/__testfixtures__/fixture6.input.ts
@@ -0,0 +1,8 @@
+import { useMemo } from 'react';
+
+function Component({ url }: { url: string }) {
+ const selectedDateMin3DaysDifference = useMemo(() => {
+ const diff = today.diff(selectedDate, "days");
+ return diff > 3 || diff < -3;
+ }, [today, selectedDate]);
+}
\ No newline at end of file
diff --git a/codemods/remove-memoization-hooks/__testfixtures__/fixture6.output.ts b/codemods/remove-memoization-hooks/__testfixtures__/fixture6.output.ts
new file mode 100644
index 0000000000000..9ed5b47588a9a
--- /dev/null
+++ b/codemods/remove-memoization-hooks/__testfixtures__/fixture6.output.ts
@@ -0,0 +1,6 @@
+function Component({ url }: { url: string }) {
+ const selectedDateMin3DaysDifference = () => {
+ const diff = today.diff(selectedDate, "days");
+ return diff > 3 || diff < -3;
+ };
+}
\ No newline at end of file
diff --git a/codemods/remove-memoization-hooks/__testfixtures__/fixture7.input.ts b/codemods/remove-memoization-hooks/__testfixtures__/fixture7.input.ts
new file mode 100644
index 0000000000000..9d4e37e5aba29
--- /dev/null
+++ b/codemods/remove-memoization-hooks/__testfixtures__/fixture7.input.ts
@@ -0,0 +1,7 @@
+import { memo, type ReactNode } from 'react';
+
+const MyComponent = ({ name } : { name: string }) => {
+ return Hello, {name}!
;
+};
+
+const MemoizedMyComponent: ReactNode = memo(MyComponent);
\ No newline at end of file
diff --git a/codemods/remove-memoization-hooks/__testfixtures__/fixture7.output.ts b/codemods/remove-memoization-hooks/__testfixtures__/fixture7.output.ts
new file mode 100644
index 0000000000000..809a24b3d1233
--- /dev/null
+++ b/codemods/remove-memoization-hooks/__testfixtures__/fixture7.output.ts
@@ -0,0 +1,7 @@
+import { type ReactNode } from 'react';
+
+const MyComponent = ({ name } : { name: string }) => {
+ return Hello, {name}!
;
+ };
+
+const MemoizedMyComponent: ReactNode = MyComponent;
\ No newline at end of file
diff --git a/codemods/remove-memoization-hooks/__testfixtures__/fixture8.input.ts b/codemods/remove-memoization-hooks/__testfixtures__/fixture8.input.ts
new file mode 100644
index 0000000000000..1fd3598a46b13
--- /dev/null
+++ b/codemods/remove-memoization-hooks/__testfixtures__/fixture8.input.ts
@@ -0,0 +1,21 @@
+import React from 'react';
+
+function Component({ url }: { url: string }) {
+ const state = React.useState();
+
+ const example1 = React.useMemo(() => {
+ const diff = today.diff(selectedDate, "days");
+ return diff > 3 || diff < -3;
+ }, [today, selectedDate]);
+
+ const example2 = React.useCallback(() => {
+ const diff = today.diff(selectedDate, "days");
+ return diff > 3 || diff < -3;
+ }, [today, selectedDate]);
+}
+
+const MyComponent = ({ name } : { name: string }) => {
+ return Hello, {name}!
;
+};
+
+const MemoizedMyComponent: React.ReactNode = React.memo(MyComponent);
\ No newline at end of file
diff --git a/codemods/remove-memoization-hooks/__testfixtures__/fixture8.output.ts b/codemods/remove-memoization-hooks/__testfixtures__/fixture8.output.ts
new file mode 100644
index 0000000000000..875c0753f3616
--- /dev/null
+++ b/codemods/remove-memoization-hooks/__testfixtures__/fixture8.output.ts
@@ -0,0 +1,21 @@
+import React from 'react';
+
+function Component({ url }: { url: string }) {
+ const state = React.useState();
+
+ const example1 = () => {
+ const diff = today.diff(selectedDate, "days");
+ return diff > 3 || diff < -3;
+ };
+
+ const example2 = () => {
+ const diff = today.diff(selectedDate, "days");
+ return diff > 3 || diff < -3;
+ };
+}
+
+const MyComponent = ({ name } : { name: string }) => {
+ return Hello, {name}!
;
+};
+
+const MemoizedMyComponent: React.ReactNode = MyComponent;
\ No newline at end of file
diff --git a/codemods/remove-memoization-hooks/__tests__/test.ts b/codemods/remove-memoization-hooks/__tests__/test.ts
new file mode 100644
index 0000000000000..dc356637a9f21
--- /dev/null
+++ b/codemods/remove-memoization-hooks/__tests__/test.ts
@@ -0,0 +1,210 @@
+import assert from 'node:assert/strict';
+import {readFile} from 'node:fs/promises';
+import {join} from 'node:path';
+import jscodeshift, {API, FileInfo} from 'jscodeshift';
+import transform from '../src/index.ts';
+
+export const buildApi = (parser: string | undefined): API => ({
+ j: parser ? jscodeshift.withParser(parser) : jscodeshift,
+ jscodeshift: parser ? jscodeshift.withParser(parser) : jscodeshift,
+ stats: () => {
+ console.error(
+ 'The stats function was called, which is not supported on purpose'
+ );
+ },
+ report: () => {
+ console.error(
+ 'The report function was called, which is not supported on purpose'
+ );
+ },
+});
+
+describe('react/19/remove-memoization-hooks', () => {
+ describe('javascript code', () => {
+ it('should remove useCallback', async () => {
+ const INPUT = await readFile(
+ join(__dirname, '..', '__testfixtures__/fixture1.input.js'),
+ 'utf-8'
+ );
+ const OUTPUT = await readFile(
+ join(__dirname, '..', '__testfixtures__/fixture1.output.js'),
+ 'utf-8'
+ );
+
+ const fileInfo: FileInfo = {
+ path: 'index.ts',
+ source: INPUT,
+ };
+
+ const actualOutput = transform(fileInfo, buildApi('js'));
+
+ assert.deepEqual(
+ actualOutput?.replace(/\W/gm, ''),
+ OUTPUT.replace(/\W/gm, '')
+ );
+ });
+
+ it('should remove useMemo', async () => {
+ const INPUT = await readFile(
+ join(__dirname, '..', '__testfixtures__/fixture2.input.js'),
+ 'utf-8'
+ );
+ const OUTPUT = await readFile(
+ join(__dirname, '..', '__testfixtures__/fixture2.output.js'),
+ 'utf-8'
+ );
+
+ const fileInfo: FileInfo = {
+ path: 'index.ts',
+ source: INPUT,
+ };
+
+ const actualOutput = transform(fileInfo, buildApi('js'));
+
+ assert.deepEqual(
+ actualOutput?.replace(/\W/gm, ''),
+ OUTPUT.replace(/\W/gm, '')
+ );
+ });
+
+ it('should remove memo', async () => {
+ const INPUT = await readFile(
+ join(__dirname, '..', '__testfixtures__/fixture3.input.js'),
+ 'utf-8'
+ );
+ const OUTPUT = await readFile(
+ join(__dirname, '..', '__testfixtures__/fixture3.output.js'),
+ 'utf-8'
+ );
+
+ const fileInfo: FileInfo = {
+ path: 'index.ts',
+ source: INPUT,
+ };
+
+ const actualOutput = transform(fileInfo, buildApi('js'));
+
+ assert.deepEqual(
+ actualOutput?.replace(/\W/gm, ''),
+ OUTPUT.replace(/\W/gm, '')
+ );
+ });
+
+ it('should remove React.useMemo, React.useCallback, React.memo', async () => {
+ const INPUT = await readFile(
+ join(__dirname, '..', '__testfixtures__/fixture4.input.js'),
+ 'utf-8'
+ );
+ const OUTPUT = await readFile(
+ join(__dirname, '..', '__testfixtures__/fixture4.output.js'),
+ 'utf-8'
+ );
+
+ const fileInfo: FileInfo = {
+ path: 'index.ts',
+ source: INPUT,
+ };
+
+ const actualOutput = transform(fileInfo, buildApi('js'));
+
+ assert.deepEqual(
+ actualOutput?.replace(/\W/gm, ''),
+ OUTPUT.replace(/\W/gm, '')
+ );
+ });
+ });
+
+ describe('typescript code', () => {
+ it('should remove useCallback', async () => {
+ const INPUT = await readFile(
+ join(__dirname, '..', '__testfixtures__/fixture5.input.ts'),
+ 'utf-8'
+ );
+ const OUTPUT = await readFile(
+ join(__dirname, '..', '__testfixtures__/fixture5.output.ts'),
+ 'utf-8'
+ );
+
+ const fileInfo: FileInfo = {
+ path: 'index.ts',
+ source: INPUT,
+ };
+
+ const actualOutput = transform(fileInfo, buildApi('tsx'));
+
+ assert.deepEqual(
+ actualOutput?.replace(/\W/gm, ''),
+ OUTPUT.replace(/\W/gm, '')
+ );
+ });
+
+ it('should remove useMemo', async () => {
+ const INPUT = await readFile(
+ join(__dirname, '..', '__testfixtures__/fixture6.input.ts'),
+ 'utf-8'
+ );
+ const OUTPUT = await readFile(
+ join(__dirname, '..', '__testfixtures__/fixture6.output.ts'),
+ 'utf-8'
+ );
+
+ const fileInfo: FileInfo = {
+ path: 'index.ts',
+ source: INPUT,
+ };
+
+ const actualOutput = transform(fileInfo, buildApi('tsx'));
+
+ assert.deepEqual(
+ actualOutput?.replace(/\W/gm, ''),
+ OUTPUT.replace(/\W/gm, '')
+ );
+ });
+
+ it('should remove memo', async () => {
+ const INPUT = await readFile(
+ join(__dirname, '..', '__testfixtures__/fixture7.input.ts'),
+ 'utf-8'
+ );
+ const OUTPUT = await readFile(
+ join(__dirname, '..', '__testfixtures__/fixture7.output.ts'),
+ 'utf-8'
+ );
+
+ const fileInfo: FileInfo = {
+ path: 'index.ts',
+ source: INPUT,
+ };
+
+ const actualOutput = transform(fileInfo, buildApi('tsx'));
+
+ assert.deepEqual(
+ actualOutput?.replace(/\W/gm, ''),
+ OUTPUT.replace(/\W/gm, '')
+ );
+ });
+
+ it('should remove React.useMemo, React.useCallback, React.memo', async () => {
+ const INPUT = await readFile(
+ join(__dirname, '..', '__testfixtures__/fixture8.input.ts'),
+ 'utf-8'
+ );
+ const OUTPUT = await readFile(
+ join(__dirname, '..', '__testfixtures__/fixture8.output.ts'),
+ 'utf-8'
+ );
+
+ const fileInfo: FileInfo = {
+ path: 'index.ts',
+ source: INPUT,
+ };
+
+ const actualOutput = transform(fileInfo, buildApi('tsx'));
+
+ assert.deepEqual(
+ actualOutput?.replace(/\W/gm, ''),
+ OUTPUT.replace(/\W/gm, '')
+ );
+ });
+ });
+});
diff --git a/codemods/remove-memoization-hooks/package.json b/codemods/remove-memoization-hooks/package.json
new file mode 100644
index 0000000000000..d85b44326be8f
--- /dev/null
+++ b/codemods/remove-memoization-hooks/package.json
@@ -0,0 +1,17 @@
+{
+ "name": "remove-memoization-hooks",
+ "version": "1.0.0",
+ "dependencies": {},
+ "devDependencies": {
+ "@types/node": "^20.12.3",
+ "@types/jscodeshift": "^0.11.10",
+ "jscodeshift": "^0.15.1",
+ "typescript": "^5.2.2"
+ },
+ "files": [
+ "./README.md",
+ "./.codemodrc.json",
+ "./dist/index.cjs"
+ ],
+ "type": "module"
+}
diff --git a/codemods/remove-memoization-hooks/src/index.ts b/codemods/remove-memoization-hooks/src/index.ts
new file mode 100644
index 0000000000000..1e5c42476668a
--- /dev/null
+++ b/codemods/remove-memoization-hooks/src/index.ts
@@ -0,0 +1,56 @@
+import type {API, FileInfo} from 'jscodeshift';
+
+export default function transform(
+ file: FileInfo,
+ api: API
+): string | undefined {
+ const j = api.jscodeshift;
+ const root = j(file.source);
+
+ const hooksToRemove = ['useMemo', 'useCallback', 'memo'];
+
+ root.find(j.ImportDeclaration).forEach(path => {
+ if (path.node.specifiers?.length === 0) {
+ return;
+ }
+
+ const specifiers =
+ path.node.specifiers?.filter(specifier => {
+ if (specifier.type === 'ImportSpecifier') {
+ return !hooksToRemove.includes(specifier.imported.name);
+ }
+ return specifier;
+ }) ?? [];
+
+ if (specifiers.length === 0) {
+ j(path).remove();
+ } else {
+ path.node.specifiers = specifiers;
+ }
+ });
+
+ hooksToRemove.forEach(hook => {
+ root
+ .find(j.CallExpression, {
+ callee: {
+ type: 'Identifier',
+ name: hook,
+ },
+ })
+ .replaceWith(path => path.value.arguments[0]);
+ });
+
+ hooksToRemove.forEach(hook => {
+ root
+ .find(j.CallExpression, {
+ callee: {
+ type: 'MemberExpression',
+ object: {name: 'React'},
+ property: {name: hook},
+ },
+ })
+ .replaceWith(path => path.value.arguments[0]);
+ });
+
+ return root.toSource();
+}
diff --git a/codemods/remove-memoization-hooks/tsconfig.json b/codemods/remove-memoization-hooks/tsconfig.json
new file mode 100644
index 0000000000000..5717e5c332cf3
--- /dev/null
+++ b/codemods/remove-memoization-hooks/tsconfig.json
@@ -0,0 +1,25 @@
+{
+ "compilerOptions": {
+ "outDir": "./dist",
+ "esModuleInterop": true,
+ "forceConsistentCasingInFileNames": true,
+ "isolatedModules": true,
+ "module": "NodeNext",
+ "skipLibCheck": true,
+ "strict": true,
+ "target": "ES6",
+ "allowJs": true,
+ "allowImportingTsExtensions": true,
+ "noEmit": true
+ },
+ "include": [
+ "./src/**/*.ts",
+ "./src/**/*.js",
+ "./__tests__/**/*.ts",
+ "./__tests__/**/*.js"
+ ],
+ "exclude": ["node_modules", "./dist/**/*"],
+ "ts-node": {
+ "transpileOnly": true
+ }
+}
\ No newline at end of file
diff --git a/codemods/replace-act-import/.codemodrc.json b/codemods/replace-act-import/.codemodrc.json
new file mode 100644
index 0000000000000..2f9338e705f7f
--- /dev/null
+++ b/codemods/replace-act-import/.codemodrc.json
@@ -0,0 +1,12 @@
+{
+ "version": "1.0.0",
+ "name": "react/19/replace-act-import",
+ "private": false,
+ "engine": "jscodeshift",
+ "meta": {
+ "tags": ["react", "migration"]
+ },
+ "applicability": {
+ "from": [["react", "<=", "18"]]
+ }
+}
diff --git a/codemods/replace-act-import/README.md b/codemods/replace-act-import/README.md
new file mode 100644
index 0000000000000..982b6743e9495
--- /dev/null
+++ b/codemods/replace-act-import/README.md
@@ -0,0 +1,42 @@
+# Replace react dom test utils with react
+
+## Description
+
+This codemod will replace the usages of `TestUtils.act()` to use `React.act()`, introduced in React v19.
+
+## Examples
+
+### Before
+
+```ts
+import { act } from 'react-dom/test-utils';
+
+act();
+```
+
+### After
+
+```ts
+import { act } from "react";
+
+act();
+```
+
+
+
+### Before
+
+```ts
+import * as ReactDOMTestUtils from 'react-dom/test-utils';
+
+ReactDOMTestUtils.act();
+```
+
+### After
+
+```ts
+import * as React from "react";
+
+React.act();
+```
+
diff --git a/codemods/replace-act-import/__testfixtures__/fixture1.input.js b/codemods/replace-act-import/__testfixtures__/fixture1.input.js
new file mode 100644
index 0000000000000..f2492fc0a4972
--- /dev/null
+++ b/codemods/replace-act-import/__testfixtures__/fixture1.input.js
@@ -0,0 +1,2 @@
+import {act} from 'react-dom/test-utils';
+act();
diff --git a/codemods/replace-act-import/__testfixtures__/fixture1.output.js b/codemods/replace-act-import/__testfixtures__/fixture1.output.js
new file mode 100644
index 0000000000000..5bd165a4686c4
--- /dev/null
+++ b/codemods/replace-act-import/__testfixtures__/fixture1.output.js
@@ -0,0 +1,2 @@
+import {act} from 'react';
+act();
diff --git a/codemods/replace-act-import/__testfixtures__/fixture2.input.js b/codemods/replace-act-import/__testfixtures__/fixture2.input.js
new file mode 100644
index 0000000000000..d77a870b7d737
--- /dev/null
+++ b/codemods/replace-act-import/__testfixtures__/fixture2.input.js
@@ -0,0 +1,2 @@
+import ReactTestUtils from 'react-dom/test-utils';
+ReactTestUtils.act();
diff --git a/codemods/replace-act-import/__testfixtures__/fixture2.output.js b/codemods/replace-act-import/__testfixtures__/fixture2.output.js
new file mode 100644
index 0000000000000..29a945350c775
--- /dev/null
+++ b/codemods/replace-act-import/__testfixtures__/fixture2.output.js
@@ -0,0 +1,2 @@
+import React from 'react';
+React.act();
diff --git a/codemods/replace-act-import/__testfixtures__/fixture3.input.js b/codemods/replace-act-import/__testfixtures__/fixture3.input.js
new file mode 100644
index 0000000000000..c8114f65590ad
--- /dev/null
+++ b/codemods/replace-act-import/__testfixtures__/fixture3.input.js
@@ -0,0 +1,2 @@
+import * as ReactTestUtils from 'react-dom/test-utils';
+ReactTestUtils.act();
diff --git a/codemods/replace-act-import/__testfixtures__/fixture3.output.js b/codemods/replace-act-import/__testfixtures__/fixture3.output.js
new file mode 100644
index 0000000000000..35875f9ae285a
--- /dev/null
+++ b/codemods/replace-act-import/__testfixtures__/fixture3.output.js
@@ -0,0 +1,2 @@
+import * as React from 'react';
+React.act();
diff --git a/codemods/replace-act-import/__testfixtures__/fixture4.input.js b/codemods/replace-act-import/__testfixtures__/fixture4.input.js
new file mode 100644
index 0000000000000..33fe1298c3d98
--- /dev/null
+++ b/codemods/replace-act-import/__testfixtures__/fixture4.input.js
@@ -0,0 +1,2 @@
+import {other} from 'react-dom/test-utils';
+other.thing();
diff --git a/codemods/replace-act-import/__testfixtures__/fixture4.output.js b/codemods/replace-act-import/__testfixtures__/fixture4.output.js
new file mode 100644
index 0000000000000..33fe1298c3d98
--- /dev/null
+++ b/codemods/replace-act-import/__testfixtures__/fixture4.output.js
@@ -0,0 +1,2 @@
+import {other} from 'react-dom/test-utils';
+other.thing();
diff --git a/codemods/replace-act-import/__testfixtures__/fixture5.input.js b/codemods/replace-act-import/__testfixtures__/fixture5.input.js
new file mode 100644
index 0000000000000..cb6a1dbaba6e0
--- /dev/null
+++ b/codemods/replace-act-import/__testfixtures__/fixture5.input.js
@@ -0,0 +1,4 @@
+import * as React from 'react';
+import * as ReactTestUtils from 'react-dom/test-utils';
+
+ReactTestUtils.act();
diff --git a/codemods/replace-act-import/__testfixtures__/fixture5.output.js b/codemods/replace-act-import/__testfixtures__/fixture5.output.js
new file mode 100644
index 0000000000000..eda28ed7fbc94
--- /dev/null
+++ b/codemods/replace-act-import/__testfixtures__/fixture5.output.js
@@ -0,0 +1,3 @@
+import * as React from 'react';
+
+React.act();
diff --git a/codemods/replace-act-import/__testfixtures__/fixture6.input.js b/codemods/replace-act-import/__testfixtures__/fixture6.input.js
new file mode 100644
index 0000000000000..ba92563c6c9ff
--- /dev/null
+++ b/codemods/replace-act-import/__testfixtures__/fixture6.input.js
@@ -0,0 +1,3 @@
+import {FC} from 'react';
+import {act} from 'react-dom/test-utils';
+act();
diff --git a/codemods/replace-act-import/__testfixtures__/fixture6.output.js b/codemods/replace-act-import/__testfixtures__/fixture6.output.js
new file mode 100644
index 0000000000000..980480b391618
--- /dev/null
+++ b/codemods/replace-act-import/__testfixtures__/fixture6.output.js
@@ -0,0 +1,2 @@
+import {FC, act} from 'react';
+act();
diff --git a/codemods/replace-act-import/__tests__/test.ts b/codemods/replace-act-import/__tests__/test.ts
new file mode 100644
index 0000000000000..71dc64946d98d
--- /dev/null
+++ b/codemods/replace-act-import/__tests__/test.ts
@@ -0,0 +1,174 @@
+import assert from 'node:assert/strict';
+import {readFile} from 'node:fs/promises';
+import {join} from 'node:path';
+import jscodeshift, {type API, type FileInfo} from 'jscodeshift';
+import transform from '../src/index.ts';
+
+export const buildApi = (parser: string | undefined): API => ({
+ j: parser ? jscodeshift.withParser(parser) : jscodeshift,
+ jscodeshift: parser ? jscodeshift.withParser(parser) : jscodeshift,
+ stats: () => {
+ console.error(
+ 'The stats function was called, which is not supported on purpose'
+ );
+ },
+ report: () => {
+ console.error(
+ 'The report function was called, which is not supported on purpose'
+ );
+ },
+});
+
+describe('react/19/replace-act-import: TestUtils.act -> React.act', () => {
+ describe('javascript code', () => {
+ it('should replace direct import with import from react', async () => {
+ const INPUT = await readFile(
+ join(__dirname, '..', '__testfixtures__/fixture1.input.js'),
+ 'utf-8'
+ );
+ const OUTPUT = await readFile(
+ join(__dirname, '..', '__testfixtures__/fixture1.output.js'),
+ 'utf-8'
+ );
+
+ const fileInfo: FileInfo = {
+ path: 'index.ts',
+ source: INPUT,
+ };
+
+ const actualOutput = transform(fileInfo, buildApi('js'), {
+ quote: 'single',
+ });
+
+ assert.deepEqual(
+ actualOutput?.replace(/\W/gm, ''),
+ OUTPUT.replace(/\W/gm, '')
+ );
+ });
+
+ it('should replace TestUtils.act with React.act', async () => {
+ const INPUT = await readFile(
+ join(__dirname, '..', '__testfixtures__/fixture2.input.js'),
+ 'utf-8'
+ );
+ const OUTPUT = await readFile(
+ join(__dirname, '..', '__testfixtures__/fixture2.output.js'),
+ 'utf-8'
+ );
+
+ const fileInfo: FileInfo = {
+ path: 'index.ts',
+ source: INPUT,
+ };
+
+ const actualOutput = transform(fileInfo, buildApi('js'), {
+ quote: 'single',
+ });
+
+ assert.deepEqual(
+ actualOutput?.replace(/\W/gm, ''),
+ OUTPUT.replace(/\W/gm, '')
+ );
+ });
+
+ it('should properly replace star import', async () => {
+ const INPUT = await readFile(
+ join(__dirname, '..', '__testfixtures__/fixture3.input.js'),
+ 'utf-8'
+ );
+ const OUTPUT = await readFile(
+ join(__dirname, '..', '__testfixtures__/fixture3.output.js'),
+ 'utf-8'
+ );
+
+ const fileInfo: FileInfo = {
+ path: 'index.ts',
+ source: INPUT,
+ };
+
+ const actualOutput = transform(fileInfo, buildApi('js'), {
+ quote: 'single',
+ });
+
+ assert.deepEqual(
+ actualOutput?.replace(/\W/gm, ''),
+ OUTPUT.replace(/\W/gm, '')
+ );
+ });
+
+ it('should not replace other imports from test utils', async () => {
+ const INPUT = await readFile(
+ join(__dirname, '..', '__testfixtures__/fixture4.input.js'),
+ 'utf-8'
+ );
+ const OUTPUT = await readFile(
+ join(__dirname, '..', '__testfixtures__/fixture4.output.js'),
+ 'utf-8'
+ );
+
+ const fileInfo: FileInfo = {
+ path: 'index.ts',
+ source: INPUT,
+ };
+
+ const actualOutput = transform(fileInfo, buildApi('js'), {
+ quote: 'single',
+ });
+
+ assert.deepEqual(
+ actualOutput?.replace(/\W/gm, ''),
+ OUTPUT.replace(/\W/gm, '')
+ );
+ });
+
+ it('should not add react import if one is already present', async () => {
+ const INPUT = await readFile(
+ join(__dirname, '..', '__testfixtures__/fixture5.input.js'),
+ 'utf-8'
+ );
+ const OUTPUT = await readFile(
+ join(__dirname, '..', '__testfixtures__/fixture5.output.js'),
+ 'utf-8'
+ );
+
+ const fileInfo: FileInfo = {
+ path: 'index.ts',
+ source: INPUT,
+ };
+
+ const actualOutput = transform(fileInfo, buildApi('js'), {
+ quote: 'single',
+ });
+
+ assert.deepEqual(
+ actualOutput?.replace(/\W/gm, ''),
+ OUTPUT.replace(/\W/gm, '')
+ );
+ });
+
+ it('should add import specifier to existing import if it exists', async () => {
+ const INPUT = await readFile(
+ join(__dirname, '..', '__testfixtures__/fixture6.input.js'),
+ 'utf-8'
+ );
+ const OUTPUT = await readFile(
+ join(__dirname, '..', '__testfixtures__/fixture6.output.js'),
+ 'utf-8'
+ );
+
+ const fileInfo: FileInfo = {
+ path: 'index.ts',
+ source: INPUT,
+ };
+
+ const actualOutput = transform(fileInfo, buildApi('js'), {
+ quote: 'single',
+ });
+
+ assert.deepEqual(
+ actualOutput?.replace(/\W/gm, ''),
+ OUTPUT.replace(/\W/gm, '')
+ );
+ });
+ });
+});
diff --git a/codemods/replace-act-import/package.json b/codemods/replace-act-import/package.json
new file mode 100644
index 0000000000000..6aaaa1adebcd1
--- /dev/null
+++ b/codemods/replace-act-import/package.json
@@ -0,0 +1,13 @@
+{
+ "name": "replace-act-import",
+ "version": "1.0.0",
+ "dependencies": {},
+ "devDependencies": {
+ "@types/node": "^20.12.3",
+ "@types/jscodeshift": "^0.11.10",
+ "jscodeshift": "^0.15.1",
+ "typescript": "^5.2.2"
+ },
+ "files": ["./README.md", "./.codemodrc.json", "./dist/index.cjs"],
+ "type": "module"
+}
diff --git a/codemods/replace-act-import/src/index.ts b/codemods/replace-act-import/src/index.ts
new file mode 100644
index 0000000000000..71d480220e721
--- /dev/null
+++ b/codemods/replace-act-import/src/index.ts
@@ -0,0 +1,156 @@
+import type {API, FileInfo, Options} from 'jscodeshift';
+
+export default function transform(
+ file: FileInfo,
+ api: API,
+ options?: Options
+): string | undefined {
+ const j = api.jscodeshift;
+ const root = j(file.source);
+
+ // Get default import from test utils
+ const defaultUtilsImportName = root
+ .find(j.ImportDeclaration, {
+ source: {value: 'react-dom/test-utils'},
+ specifiers: [{type: 'ImportDefaultSpecifier'}],
+ })
+ .paths()
+ .at(0)
+ ?.node.specifiers?.at(0)?.local?.name;
+
+ // Get default import from test utils
+ const starUtilsImportName = root
+ .find(j.ImportDeclaration, {
+ source: {value: 'react-dom/test-utils'},
+ specifiers: [{type: 'ImportNamespaceSpecifier'}],
+ })
+ .paths()
+ .at(0)
+ ?.node.specifiers?.at(0)?.local?.name;
+
+ const utilsCalleeName = defaultUtilsImportName ?? starUtilsImportName;
+ const utilsCalleeType: any = defaultUtilsImportName
+ ? 'ImportDefaultSpecifier'
+ : 'ImportNamespaceSpecifier';
+
+ // For usages like `import * as ReactTestUtils from 'react-dom/test-utils'; ReactTestUtils.act()`
+ const actAccessExpressions = root.find(j.MemberExpression, {
+ object: {name: utilsCalleeName},
+ property: {name: 'act'},
+ });
+
+ if (actAccessExpressions.length > 0) {
+ // React import
+ const defaultReactImportName = root
+ .find(j.ImportDeclaration, {source: {value: 'react'}})
+ .paths()
+ .at(0)
+ ?.node.specifiers?.at(0)?.local?.name;
+
+ if (!defaultReactImportName) {
+ const importNode =
+ utilsCalleeType === 'ImportDefaultSpecifier'
+ ? j.importDefaultSpecifier
+ : j.importNamespaceSpecifier;
+
+ const reactImport = j.importDeclaration(
+ [importNode(j.identifier('React'))],
+ j.literal('react')
+ );
+
+ root.get().node.program.body.unshift(reactImport);
+ }
+
+ actAccessExpressions.forEach(path => {
+ const accessedPath = j(path)
+ .find(j.Identifier, {name: utilsCalleeName})
+ .paths()
+ .at(0);
+
+ const newIdentifier = j.identifier.from({
+ name: defaultReactImportName ?? 'React',
+ });
+
+ accessedPath?.replace(newIdentifier);
+ });
+
+ // Remove the old import
+ root
+ .find(j.ImportDeclaration, {
+ source: {value: 'react-dom/test-utils'},
+ specifiers: [{type: utilsCalleeType}],
+ })
+ .remove();
+ }
+
+ // Get act direct import from test utils
+ // const actImport = root
+ // .find(j.ImportDeclaration, {
+ // source: {value: 'react-dom/test-utils'},
+ // specifiers: [{type: 'ImportSpecifier', imported: {name: 'act'}}],
+ // })
+ // .at(0);
+
+ // if (actImport) {
+ // // actImport.remove();
+ // // const reactImport = root
+ // // .find(j.ImportDeclaration, {
+ // // source: {value: 'react'},
+ // // specifiers: [{type: 'ImportSpecifier'}],
+ // // })
+ // // .paths()
+ // // .at(0);
+
+ // // if (!reactImport) {
+ // // const newReactImport = j.importDeclaration(
+ // // [j.importSpecifier(j.identifier('act'))],
+ // // j.literal('react')
+ // // );
+
+ // // root.find(j.VariableDeclaration).at(0).get().insertBefore(newReactImport);
+ // // } else {
+ // // const newImportSpecifier = j.importSpecifier(
+ // // j.identifier('act'),
+ // // j.identifier('act')
+ // // );
+
+ // // reactImport?.node.specifiers?.push(newImportSpecifier);
+ // // }
+
+ // }
+
+ root
+ .find(j.ImportDeclaration, {
+ source: {value: 'react-dom/test-utils'},
+ specifiers: [{type: 'ImportSpecifier', imported: {name: 'act'}}],
+ })
+ .forEach(path => {
+ const newImportSpecifier = j.importSpecifier(
+ j.identifier('act'),
+ j.identifier('act')
+ );
+
+ const existingReactImportCollection = root.find(j.ImportDeclaration, {
+ source: {value: 'react'},
+ specifiers: [{type: 'ImportSpecifier'}],
+ });
+
+ if (existingReactImportCollection.length > 0) {
+ existingReactImportCollection
+ .paths()
+ .at(0)
+ ?.node.specifiers?.push(newImportSpecifier);
+
+ path.prune();
+ } else {
+ const newImportDeclaration = j.importDeclaration(
+ [newImportSpecifier],
+ j.literal('react')
+ );
+
+ path.replace(newImportDeclaration);
+ }
+ });
+
+ return root.toSource();
+}
diff --git a/codemods/replace-act-import/tsconfig.json b/codemods/replace-act-import/tsconfig.json
new file mode 100644
index 0000000000000..5717e5c332cf3
--- /dev/null
+++ b/codemods/replace-act-import/tsconfig.json
@@ -0,0 +1,25 @@
+{
+ "compilerOptions": {
+ "outDir": "./dist",
+ "esModuleInterop": true,
+ "forceConsistentCasingInFileNames": true,
+ "isolatedModules": true,
+ "module": "NodeNext",
+ "skipLibCheck": true,
+ "strict": true,
+ "target": "ES6",
+ "allowJs": true,
+ "allowImportingTsExtensions": true,
+ "noEmit": true
+ },
+ "include": [
+ "./src/**/*.ts",
+ "./src/**/*.js",
+ "./__tests__/**/*.ts",
+ "./__tests__/**/*.js"
+ ],
+ "exclude": ["node_modules", "./dist/**/*"],
+ "ts-node": {
+ "transpileOnly": true
+ }
+}
\ No newline at end of file
diff --git a/codemods/replace-use-form-state/.codemodrc.json b/codemods/replace-use-form-state/.codemodrc.json
new file mode 100644
index 0000000000000..9deaabf4c236f
--- /dev/null
+++ b/codemods/replace-use-form-state/.codemodrc.json
@@ -0,0 +1,12 @@
+{
+ "version": "1.0.0",
+ "name": "react/19/replace-use-form-state",
+ "private": false,
+ "engine": "jscodeshift",
+ "meta": {
+ "tags": ["react", "migration"]
+ },
+ "applicability": {
+ "from": [["react", "<=", "18"]]
+ }
+}
diff --git a/codemods/replace-use-form-state/README.md b/codemods/replace-use-form-state/README.md
new file mode 100644
index 0000000000000..58c4cc19521e4
--- /dev/null
+++ b/codemods/replace-use-form-state/README.md
@@ -0,0 +1,72 @@
+# Replace react useFormState with useActionState
+
+## Description
+
+This codemod will replace the usages of `useFormState()` to use `useActionState()`, introduced in React v19.
+
+## Examples
+
+### Before
+
+```ts
+import { useFormState } from "react-dom";
+
+async function increment(previousState, formData) {
+ return previousState + 1;
+}
+
+function StatefulForm({}) {
+ const [state, formAction] = useFormState(increment, 0);
+ return (
+
+ )
+}
+```
+
+### After
+
+```ts
+import { useActionState } from "react-dom";
+
+async function increment(previousState, formData) {
+ return previousState + 1;
+}
+
+function StatefulForm({}) {
+ const [state, formAction] = useActionState(increment, 0);
+ return (
+
+ )
+}
+```
+
+
+
+### Before
+
+```ts
+import * as ReactDOM from "react-dom";
+
+function StatefulForm({}) {
+ const [state, formAction] = ReactDOM.useFormState(increment, 0);
+ return ;
+}
+```
+
+### After
+
+```ts
+import * as ReactDOM from "react-dom";
+
+function StatefulForm({}) {
+ const [state, formAction] = ReactDOM.useActionState(increment, 0);
+ return ;
+}
+```
+
diff --git a/codemods/replace-use-form-state/__testfixtures__/fixture1.input.js b/codemods/replace-use-form-state/__testfixtures__/fixture1.input.js
new file mode 100644
index 0000000000000..0ddc910f33d98
--- /dev/null
+++ b/codemods/replace-use-form-state/__testfixtures__/fixture1.input.js
@@ -0,0 +1,15 @@
+import { useFormState } from "react-dom";
+
+async function increment(previousState, formData) {
+ return previousState + 1;
+}
+
+function StatefulForm({}) {
+ const [state, formAction] = useFormState(increment, 0);
+ return (
+
+ );
+}
diff --git a/codemods/replace-use-form-state/__testfixtures__/fixture1.output.js b/codemods/replace-use-form-state/__testfixtures__/fixture1.output.js
new file mode 100644
index 0000000000000..14c66038f2fc9
--- /dev/null
+++ b/codemods/replace-use-form-state/__testfixtures__/fixture1.output.js
@@ -0,0 +1,15 @@
+import { useActionState } from "react-dom";
+
+async function increment(previousState, formData) {
+ return previousState + 1;
+}
+
+function StatefulForm({}) {
+ const [state, formAction] = useActionState(increment, 0);
+ return (
+
+ );
+}
diff --git a/codemods/replace-use-form-state/__testfixtures__/fixture2.input.js b/codemods/replace-use-form-state/__testfixtures__/fixture2.input.js
new file mode 100644
index 0000000000000..645ac55720dc9
--- /dev/null
+++ b/codemods/replace-use-form-state/__testfixtures__/fixture2.input.js
@@ -0,0 +1,6 @@
+import ReactDOM from "react-dom";
+
+function StatefulForm({}) {
+ const [state, formAction] = ReactDOM.useFormState(increment, 0);
+ return ;
+}
diff --git a/codemods/replace-use-form-state/__testfixtures__/fixture2.output.js b/codemods/replace-use-form-state/__testfixtures__/fixture2.output.js
new file mode 100644
index 0000000000000..d185a4e1bfcdd
--- /dev/null
+++ b/codemods/replace-use-form-state/__testfixtures__/fixture2.output.js
@@ -0,0 +1,6 @@
+import ReactDOM from "react-dom";
+
+function StatefulForm({}) {
+ const [state, formAction] = ReactDOM.useActionState(increment, 0);
+ return ;
+}
diff --git a/codemods/replace-use-form-state/__testfixtures__/fixture3.input.js b/codemods/replace-use-form-state/__testfixtures__/fixture3.input.js
new file mode 100644
index 0000000000000..48968985d27ee
--- /dev/null
+++ b/codemods/replace-use-form-state/__testfixtures__/fixture3.input.js
@@ -0,0 +1,6 @@
+import * as ReactDOM from "react-dom";
+
+function StatefulForm({}) {
+ const [state, formAction] = ReactDOM.useFormState(increment, 0);
+ return ;
+}
diff --git a/codemods/replace-use-form-state/__testfixtures__/fixture3.output.js b/codemods/replace-use-form-state/__testfixtures__/fixture3.output.js
new file mode 100644
index 0000000000000..1375f0adbfe8a
--- /dev/null
+++ b/codemods/replace-use-form-state/__testfixtures__/fixture3.output.js
@@ -0,0 +1,6 @@
+import * as ReactDOM from "react-dom";
+
+function StatefulForm({}) {
+ const [state, formAction] = ReactDOM.useActionState(increment, 0);
+ return ;
+}
diff --git a/codemods/replace-use-form-state/__testfixtures__/fixture4.input.js b/codemods/replace-use-form-state/__testfixtures__/fixture4.input.js
new file mode 100644
index 0000000000000..a882243aa46a6
--- /dev/null
+++ b/codemods/replace-use-form-state/__testfixtures__/fixture4.input.js
@@ -0,0 +1,6 @@
+import { other } from "react-dom";
+
+function StatefulForm({}) {
+ const otherResult = other();
+ return ;
+}
diff --git a/codemods/replace-use-form-state/__testfixtures__/fixture4.output.js b/codemods/replace-use-form-state/__testfixtures__/fixture4.output.js
new file mode 100644
index 0000000000000..a882243aa46a6
--- /dev/null
+++ b/codemods/replace-use-form-state/__testfixtures__/fixture4.output.js
@@ -0,0 +1,6 @@
+import { other } from "react-dom";
+
+function StatefulForm({}) {
+ const otherResult = other();
+ return ;
+}
diff --git a/codemods/replace-use-form-state/__testfixtures__/fixture5.input.js b/codemods/replace-use-form-state/__testfixtures__/fixture5.input.js
new file mode 100644
index 0000000000000..cd95cc72bad1b
--- /dev/null
+++ b/codemods/replace-use-form-state/__testfixtures__/fixture5.input.js
@@ -0,0 +1,8 @@
+import { createPortal, useFormState } from "react-dom";
+
+function StatefulForm({}) {
+ const [state, formAction] = useFormState(increment, 0);
+
+ createPortal();
+ return ;
+}
diff --git a/codemods/replace-use-form-state/__testfixtures__/fixture5.output.js b/codemods/replace-use-form-state/__testfixtures__/fixture5.output.js
new file mode 100644
index 0000000000000..14d336c1a79a0
--- /dev/null
+++ b/codemods/replace-use-form-state/__testfixtures__/fixture5.output.js
@@ -0,0 +1,8 @@
+import { createPortal, useActionState } from "react-dom";
+
+function StatefulForm({}) {
+ const [state, formAction] = useActionState(increment, 0);
+
+ createPortal();
+ return ;
+}
diff --git a/codemods/replace-use-form-state/__testfixtures__/fixture6.input.js b/codemods/replace-use-form-state/__testfixtures__/fixture6.input.js
new file mode 100644
index 0000000000000..afe791daa21f0
--- /dev/null
+++ b/codemods/replace-use-form-state/__testfixtures__/fixture6.input.js
@@ -0,0 +1,8 @@
+import { createPortal, useFormState as UFS } from "react-dom";
+
+function StatefulForm({}) {
+ const [state, formAction] = UFS(increment, 0);
+
+ createPortal();
+ return ;
+}
diff --git a/codemods/replace-use-form-state/__testfixtures__/fixture6.output.js b/codemods/replace-use-form-state/__testfixtures__/fixture6.output.js
new file mode 100644
index 0000000000000..f0772cb801ebd
--- /dev/null
+++ b/codemods/replace-use-form-state/__testfixtures__/fixture6.output.js
@@ -0,0 +1,8 @@
+import { createPortal, useActionState as UFS } from "react-dom";
+
+function StatefulForm({}) {
+ const [state, formAction] = UFS(increment, 0);
+
+ createPortal();
+ return ;
+}
diff --git a/codemods/replace-use-form-state/__tests__/test.ts b/codemods/replace-use-form-state/__tests__/test.ts
new file mode 100644
index 0000000000000..49714c3bcf8a6
--- /dev/null
+++ b/codemods/replace-use-form-state/__tests__/test.ts
@@ -0,0 +1,172 @@
+import assert from 'node:assert/strict';
+import {readFile} from 'node:fs/promises';
+import {join} from 'node:path';
+import jscodeshift, {type API, type FileInfo} from 'jscodeshift';
+import transform from '../src/index.ts';
+
+export const buildApi = (parser: string | undefined): API => ({
+ j: parser ? jscodeshift.withParser(parser) : jscodeshift,
+ jscodeshift: parser ? jscodeshift.withParser(parser) : jscodeshift,
+ stats: () => {
+ console.error(
+ 'The stats function was called, which is not supported on purpose'
+ );
+ },
+ report: () => {
+ console.error(
+ 'The report function was called, which is not supported on purpose'
+ );
+ },
+});
+
+describe('react/19/replace-use-form-state: useFormState() -> useActionState()', () => {
+ it('should replace direct import with new useActionState import', async () => {
+ const INPUT = await readFile(
+ join(__dirname, '..', '__testfixtures__/fixture1.input.js'),
+ 'utf-8'
+ );
+ const OUTPUT = await readFile(
+ join(__dirname, '..', '__testfixtures__/fixture1.output.js'),
+ 'utf-8'
+ );
+
+ const fileInfo: FileInfo = {
+ path: 'index.ts',
+ source: INPUT,
+ };
+
+ const actualOutput = transform(fileInfo, buildApi('js'), {
+ quote: 'single',
+ });
+
+ assert.deepEqual(
+ actualOutput?.replace(/\W/gm, ''),
+ OUTPUT.replace(/\W/gm, '')
+ );
+ });
+
+ it('should replace ReactDOM.useFormState with ReactDOM.useActionState', async () => {
+ const INPUT = await readFile(
+ join(__dirname, '..', '__testfixtures__/fixture2.input.js'),
+ 'utf-8'
+ );
+ const OUTPUT = await readFile(
+ join(__dirname, '..', '__testfixtures__/fixture2.output.js'),
+ 'utf-8'
+ );
+
+ const fileInfo: FileInfo = {
+ path: 'index.ts',
+ source: INPUT,
+ };
+
+ const actualOutput = transform(fileInfo, buildApi('js'), {
+ quote: 'single',
+ });
+
+ assert.deepEqual(
+ actualOutput?.replace(/\W/gm, ''),
+ OUTPUT.replace(/\W/gm, '')
+ );
+ });
+
+ it('should properly replace star import', async () => {
+ const INPUT = await readFile(
+ join(__dirname, '..', '__testfixtures__/fixture3.input.js'),
+ 'utf-8'
+ );
+ const OUTPUT = await readFile(
+ join(__dirname, '..', '__testfixtures__/fixture3.output.js'),
+ 'utf-8'
+ );
+
+ const fileInfo: FileInfo = {
+ path: 'index.ts',
+ source: INPUT,
+ };
+
+ const actualOutput = transform(fileInfo, buildApi('js'), {
+ quote: 'single',
+ });
+
+ assert.deepEqual(
+ actualOutput?.replace(/\W/gm, ''),
+ OUTPUT.replace(/\W/gm, '')
+ );
+ });
+
+ it('should not replace other imports react-dom', async () => {
+ const INPUT = await readFile(
+ join(__dirname, '..', '__testfixtures__/fixture4.input.js'),
+ 'utf-8'
+ );
+ const OUTPUT = await readFile(
+ join(__dirname, '..', '__testfixtures__/fixture4.output.js'),
+ 'utf-8'
+ );
+
+ const fileInfo: FileInfo = {
+ path: 'index.ts',
+ source: INPUT,
+ };
+
+ const actualOutput = transform(fileInfo, buildApi('js'), {
+ quote: 'single',
+ });
+
+ assert.deepEqual(
+ actualOutput?.replace(/\W/gm, ''),
+ OUTPUT.replace(/\W/gm, '')
+ );
+ });
+
+ it('should add useActionState import in existing import clause if other imports are present', async () => {
+ const INPUT = await readFile(
+ join(__dirname, '..', '__testfixtures__/fixture5.input.js'),
+ 'utf-8'
+ );
+ const OUTPUT = await readFile(
+ join(__dirname, '..', '__testfixtures__/fixture5.output.js'),
+ 'utf-8'
+ );
+
+ const fileInfo: FileInfo = {
+ path: 'index.ts',
+ source: INPUT,
+ };
+
+ const actualOutput = transform(fileInfo, buildApi('js'), {
+ quote: 'single',
+ });
+
+ assert.deepEqual(
+ actualOutput?.replace(/\W/gm, ''),
+ OUTPUT.replace(/\W/gm, '')
+ );
+ });
+
+ it('should correctly work with aliased imports', async () => {
+ const INPUT = await readFile(
+ join(__dirname, '..', '__testfixtures__/fixture6.input.js'),
+ 'utf-8'
+ );
+ const OUTPUT = await readFile(
+ join(__dirname, '..', '__testfixtures__/fixture6.output.js'),
+ 'utf-8'
+ );
+
+ const fileInfo: FileInfo = {
+ path: 'index.ts',
+ source: INPUT,
+ };
+
+ const actualOutput = transform(fileInfo, buildApi('js'), {
+ quote: 'single',
+ });
+
+ assert.deepEqual(
+ actualOutput?.replace(/\W/gm, ''),
+ OUTPUT.replace(/\W/gm, '')
+ );
+ });
+});
diff --git a/codemods/replace-use-form-state/package.json b/codemods/replace-use-form-state/package.json
new file mode 100644
index 0000000000000..e7cb152456f36
--- /dev/null
+++ b/codemods/replace-use-form-state/package.json
@@ -0,0 +1,13 @@
+{
+ "name": "replace-use-form-state",
+ "version": "1.0.0",
+ "dependencies": {},
+ "devDependencies": {
+ "@types/node": "^20.12.3",
+ "@types/jscodeshift": "^0.11.10",
+ "jscodeshift": "^0.15.1",
+ "typescript": "^5.2.2"
+ },
+ "files": ["./README.md", "./.codemodrc.json", "./dist/index.cjs"],
+ "type": "module"
+}
diff --git a/codemods/replace-use-form-state/src/index.ts b/codemods/replace-use-form-state/src/index.ts
new file mode 100644
index 0000000000000..29771a7d0fd4f
--- /dev/null
+++ b/codemods/replace-use-form-state/src/index.ts
@@ -0,0 +1,95 @@
+import type { API, FileInfo, Options } from "jscodeshift";
+
+export default function transform(
+ file: FileInfo,
+ api: API,
+ options?: Options,
+): string | undefined {
+ const j = api.jscodeshift;
+ const root = j(file.source);
+
+ // Get default import from react-dom
+ const defaultImportName = root
+ .find(j.ImportDeclaration, {
+ source: { value: "react-dom" },
+ specifiers: [{ type: "ImportDefaultSpecifier" }],
+ })
+ .paths()
+ .at(0)
+ ?.node.specifiers?.at(0)?.local?.name;
+
+ // Get default import from test utils
+ const starImportName = root
+ .find(j.ImportDeclaration, {
+ source: { value: "react-dom" },
+ specifiers: [{ type: "ImportNamespaceSpecifier" }],
+ })
+ .paths()
+ .at(0)
+ ?.node.specifiers?.at(0)?.local?.name;
+
+ const utilsCalleeName = defaultImportName ?? starImportName;
+ const utilsCalleeType: any = defaultImportName
+ ? "ImportDefaultSpecifier"
+ : "ImportNamespaceSpecifier";
+
+ // For usages like `import * as ReactDOM from 'react-dom'; ReactDOM.useFormState()`
+ const actAccessExpressions = root.find(j.MemberExpression, {
+ object: { name: utilsCalleeName },
+ property: { name: "useFormState" },
+ });
+
+ if (actAccessExpressions.length > 0) {
+ // React import
+
+ actAccessExpressions.forEach((path) => {
+ j(path)
+ .find(j.Identifier, { name: "useFormState" })
+ .paths()
+ .at(0)
+ ?.replace(j.identifier("useActionState"));
+ });
+ }
+
+ // For direct imports, such as `import { useFormState } from 'react-dom';`
+ const reactDOMImportCollection = root.find(j.ImportDeclaration, {
+ source: { value: "react-dom" },
+ });
+
+ const reactDOMImportPath = reactDOMImportCollection.paths().at(0);
+
+ if (!reactDOMImportPath) {
+ return root.toSource();
+ }
+
+ const specifier = reactDOMImportPath.node.specifiers?.find(
+ (s) => s.type === "ImportSpecifier" && s.imported.name === "useFormState",
+ );
+
+ if (!specifier || !j.ImportSpecifier.check(specifier)) {
+ return root.toSource();
+ }
+
+ const usedName = specifier.local?.name ?? specifier.imported.name;
+
+ // Replace import name
+ reactDOMImportCollection
+ .find(j.ImportSpecifier, { imported: { name: "useFormState" } })
+ .forEach((path) => {
+ path.replace(
+ j.importSpecifier(
+ j.identifier("useActionState"),
+ j.identifier(usedName),
+ ),
+ );
+ });
+
+ // Means it's not aliased, so we also change identifier names, not only import
+ if (specifier?.local?.name === "useFormState") {
+ root.find(j.Identifier, { name: "useFormState" }).forEach((path) => {
+ path.replace(j.identifier("useActionState"));
+ });
+ }
+
+ return root.toSource();
+}
diff --git a/codemods/replace-use-form-state/tsconfig.json b/codemods/replace-use-form-state/tsconfig.json
new file mode 100644
index 0000000000000..2cda28bff22d3
--- /dev/null
+++ b/codemods/replace-use-form-state/tsconfig.json
@@ -0,0 +1,25 @@
+{
+ "compilerOptions": {
+ "outDir": "./dist",
+ "esModuleInterop": true,
+ "forceConsistentCasingInFileNames": true,
+ "isolatedModules": true,
+ "module": "NodeNext",
+ "skipLibCheck": true,
+ "strict": true,
+ "target": "ES6",
+ "allowJs": true,
+ "allowImportingTsExtensions": true,
+ "noEmit": true
+ },
+ "include": [
+ "./src/**/*.ts",
+ "./src/**/*.js",
+ "__tests__/**/*.ts",
+ "__tests__/**/*.js"
+ ],
+ "exclude": ["node_modules", "./dist/**/*"],
+ "ts-node": {
+ "transpileOnly": true
+ }
+}
diff --git a/codemods/use-context-hook/.codemodrc.json b/codemods/use-context-hook/.codemodrc.json
new file mode 100644
index 0000000000000..0640bf04ae492
--- /dev/null
+++ b/codemods/use-context-hook/.codemodrc.json
@@ -0,0 +1,12 @@
+{
+ "version": "1.0.0",
+ "name": "react/19/use-context-hook",
+ "private": false,
+ "engine": "jscodeshift",
+ "meta": {
+ "tags": ["react", "migration"]
+ },
+ "applicability": {
+ "from": [["react", "<=", "18"]]
+ }
+}
diff --git a/codemods/use-context-hook/README.md b/codemods/use-context-hook/README.md
new file mode 100644
index 0000000000000..14b6f0040a57b
--- /dev/null
+++ b/codemods/use-context-hook/README.md
@@ -0,0 +1,25 @@
+# Change useContext usage to use hook
+
+## Description
+
+This codemod will convert the usage of `useContext` to the new hook format, introduced in React v19.
+
+## Example
+
+### Before:
+
+```tsx
+import { useContext } from "react";
+import ThemeContext from "./ThemeContext";
+
+const theme = useContext(ThemeContext);
+```
+
+### After:
+
+```tsx
+import { use } from "react";
+import ThemeContext from "./ThemeContext";
+
+const theme = use(ThemeContext);
+```
diff --git a/codemods/use-context-hook/__testfixtures__/fixture1.input.js b/codemods/use-context-hook/__testfixtures__/fixture1.input.js
new file mode 100644
index 0000000000000..979a8e471ca91
--- /dev/null
+++ b/codemods/use-context-hook/__testfixtures__/fixture1.input.js
@@ -0,0 +1,4 @@
+import { useContext } from "react";
+import ThemeContext from "./ThemeContext";
+
+const theme = useContext(ThemeContext);
\ No newline at end of file
diff --git a/codemods/use-context-hook/__testfixtures__/fixture1.output.js b/codemods/use-context-hook/__testfixtures__/fixture1.output.js
new file mode 100644
index 0000000000000..e251b5734be14
--- /dev/null
+++ b/codemods/use-context-hook/__testfixtures__/fixture1.output.js
@@ -0,0 +1,4 @@
+import { use } from "react";
+import ThemeContext from "./ThemeContext";
+
+const theme = use(ThemeContext);
\ No newline at end of file
diff --git a/codemods/use-context-hook/__testfixtures__/fixture2.input.js b/codemods/use-context-hook/__testfixtures__/fixture2.input.js
new file mode 100644
index 0000000000000..67718d9887320
--- /dev/null
+++ b/codemods/use-context-hook/__testfixtures__/fixture2.input.js
@@ -0,0 +1,4 @@
+import React from "react";
+import ThemeContext from "./ThemeContext";
+
+const theme = React.useContext(ThemeContext);
\ No newline at end of file
diff --git a/codemods/use-context-hook/__testfixtures__/fixture2.output.js b/codemods/use-context-hook/__testfixtures__/fixture2.output.js
new file mode 100644
index 0000000000000..f7f504f5d558b
--- /dev/null
+++ b/codemods/use-context-hook/__testfixtures__/fixture2.output.js
@@ -0,0 +1,4 @@
+import React from "react";
+import ThemeContext from "./ThemeContext";
+
+const theme = React.use(ThemeContext);
\ No newline at end of file
diff --git a/codemods/use-context-hook/__testfixtures__/fixture3.input.js b/codemods/use-context-hook/__testfixtures__/fixture3.input.js
new file mode 100644
index 0000000000000..75bfeb59b5ffc
--- /dev/null
+++ b/codemods/use-context-hook/__testfixtures__/fixture3.input.js
@@ -0,0 +1 @@
+const theme = trpc.useContext();
\ No newline at end of file
diff --git a/codemods/use-context-hook/__testfixtures__/fixture3.output.js b/codemods/use-context-hook/__testfixtures__/fixture3.output.js
new file mode 100644
index 0000000000000..75bfeb59b5ffc
--- /dev/null
+++ b/codemods/use-context-hook/__testfixtures__/fixture3.output.js
@@ -0,0 +1 @@
+const theme = trpc.useContext();
\ No newline at end of file
diff --git a/codemods/use-context-hook/__testfixtures__/fixture4.input.ts b/codemods/use-context-hook/__testfixtures__/fixture4.input.ts
new file mode 100644
index 0000000000000..74640584c78f5
--- /dev/null
+++ b/codemods/use-context-hook/__testfixtures__/fixture4.input.ts
@@ -0,0 +1,11 @@
+import { useContext } from "react";
+import ThemeContext from "./ThemeContext";
+
+function Component({
+ appUrl,
+}: {
+ appUrl: string;
+}) {
+ const theme = useContext(ThemeContext);
+ return ;
+};
\ No newline at end of file
diff --git a/codemods/use-context-hook/__testfixtures__/fixture4.output.ts b/codemods/use-context-hook/__testfixtures__/fixture4.output.ts
new file mode 100644
index 0000000000000..d44d7ae85ee2e
--- /dev/null
+++ b/codemods/use-context-hook/__testfixtures__/fixture4.output.ts
@@ -0,0 +1,11 @@
+import { use } from "react";
+import ThemeContext from "./ThemeContext";
+
+function Component({
+ appUrl,
+}: {
+ appUrl: string;
+}) {
+ const theme = use(ThemeContext);
+ return ;
+};
\ No newline at end of file
diff --git a/codemods/use-context-hook/__testfixtures__/fixture5.input.ts b/codemods/use-context-hook/__testfixtures__/fixture5.input.ts
new file mode 100644
index 0000000000000..062dab59bdf85
--- /dev/null
+++ b/codemods/use-context-hook/__testfixtures__/fixture5.input.ts
@@ -0,0 +1,11 @@
+import React from "react";
+import ThemeContext from "./ThemeContext";
+
+function Component({
+ appUrl,
+}: {
+ appUrl: string;
+}) {
+ const theme = React.useContext(ThemeContext);
+ return ;
+};
\ No newline at end of file
diff --git a/codemods/use-context-hook/__testfixtures__/fixture5.output.ts b/codemods/use-context-hook/__testfixtures__/fixture5.output.ts
new file mode 100644
index 0000000000000..a873ec9a8491f
--- /dev/null
+++ b/codemods/use-context-hook/__testfixtures__/fixture5.output.ts
@@ -0,0 +1,11 @@
+import React from "react";
+import ThemeContext from "./ThemeContext";
+
+function Component({
+ appUrl,
+}: {
+ appUrl: string;
+}) {
+ const theme = React.use(ThemeContext);
+ return ;
+};
\ No newline at end of file
diff --git a/codemods/use-context-hook/__testfixtures__/fixture6.input.ts b/codemods/use-context-hook/__testfixtures__/fixture6.input.ts
new file mode 100644
index 0000000000000..88397ec83f204
--- /dev/null
+++ b/codemods/use-context-hook/__testfixtures__/fixture6.input.ts
@@ -0,0 +1,8 @@
+function Component({
+ appUrl,
+}: {
+ appUrl: string;
+}) {
+ const theme = trpc.useContext();
+ return ;
+};
\ No newline at end of file
diff --git a/codemods/use-context-hook/__testfixtures__/fixture6.output.ts b/codemods/use-context-hook/__testfixtures__/fixture6.output.ts
new file mode 100644
index 0000000000000..88397ec83f204
--- /dev/null
+++ b/codemods/use-context-hook/__testfixtures__/fixture6.output.ts
@@ -0,0 +1,8 @@
+function Component({
+ appUrl,
+}: {
+ appUrl: string;
+}) {
+ const theme = trpc.useContext();
+ return ;
+};
\ No newline at end of file
diff --git a/codemods/use-context-hook/__tests__/test.ts b/codemods/use-context-hook/__tests__/test.ts
new file mode 100644
index 0000000000000..cf538a14990a0
--- /dev/null
+++ b/codemods/use-context-hook/__tests__/test.ts
@@ -0,0 +1,176 @@
+import assert from 'node:assert/strict';
+import {readFile} from 'node:fs/promises';
+import {join} from 'node:path';
+import jscodeshift, { type API, type FileInfo} from 'jscodeshift';
+import transform from '../src/index.ts';
+
+export const buildApi = (parser: string | undefined): API => ({
+ j: parser ? jscodeshift.withParser(parser) : jscodeshift,
+ jscodeshift: parser ? jscodeshift.withParser(parser) : jscodeshift,
+ stats: () => {
+ console.error(
+ 'The stats function was called, which is not supported on purpose'
+ );
+ },
+ report: () => {
+ console.error(
+ 'The report function was called, which is not supported on purpose'
+ );
+ },
+});
+
+describe('react/19/use-context-hook: useContext -> use', () => {
+ describe('javascript code', () => {
+ it('should replace useContext with use', async () => {
+ const INPUT = await readFile(
+ join(__dirname, '..', '__testfixtures__/fixture1.input.js'),
+ 'utf-8'
+ );
+ const OUTPUT = await readFile(
+ join(__dirname, '..', '__testfixtures__/fixture1.output.js'),
+ 'utf-8'
+ );
+
+ const fileInfo: FileInfo = {
+ path: 'index.ts',
+ source: INPUT,
+ };
+
+ const actualOutput = transform(fileInfo, buildApi('js'), {
+ quote: 'single',
+ });
+
+ assert.deepEqual(
+ actualOutput?.replace(/\W/gm, ''),
+ OUTPUT.replace(/\W/gm, '')
+ );
+ });
+
+ it('should replace React.useContext with use', async () => {
+ const INPUT = await readFile(
+ join(__dirname, '..', '__testfixtures__/fixture2.input.js'),
+ 'utf-8'
+ );
+ const OUTPUT = await readFile(
+ join(__dirname, '..', '__testfixtures__/fixture2.output.js'),
+ 'utf-8'
+ );
+
+ const fileInfo: FileInfo = {
+ path: 'index.ts',
+ source: INPUT,
+ };
+
+ const actualOutput = transform(fileInfo, buildApi('js'), {
+ quote: 'single',
+ });
+
+ assert.deepEqual(
+ actualOutput?.replace(/\W/gm, ''),
+ OUTPUT.replace(/\W/gm, '')
+ );
+ });
+
+ it('should not replace any.useContext() with use', async () => {
+ const INPUT = await readFile(
+ join(__dirname, '..', '__testfixtures__/fixture3.input.js'),
+ 'utf-8'
+ );
+ const OUTPUT = await readFile(
+ join(__dirname, '..', '__testfixtures__/fixture3.output.js'),
+ 'utf-8'
+ );
+
+ const fileInfo: FileInfo = {
+ path: 'index.ts',
+ source: INPUT,
+ };
+
+ const actualOutput = transform(fileInfo, buildApi('js'), {
+ quote: 'single',
+ });
+
+ assert.deepEqual(
+ actualOutput?.replace(/\W/gm, ''),
+ OUTPUT.replace(/\W/gm, '')
+ );
+ });
+ });
+
+ describe('typescript code', () => {
+ it('should replace useContext with use', async () => {
+ const INPUT = await readFile(
+ join(__dirname, '..', '__testfixtures__/fixture4.input.ts'),
+ 'utf-8'
+ );
+ const OUTPUT = await readFile(
+ join(__dirname, '..', '__testfixtures__/fixture4.output.ts'),
+ 'utf-8'
+ );
+
+ const fileInfo: FileInfo = {
+ path: 'index.ts',
+ source: INPUT,
+ };
+
+ const actualOutput = transform(fileInfo, buildApi('tsx'), {
+ quote: 'single',
+ });
+
+ assert.deepEqual(
+ actualOutput?.replace(/\W/gm, ''),
+ OUTPUT.replace(/\W/gm, '')
+ );
+ });
+
+ it('should replace React.useContext with use', async () => {
+ const INPUT = await readFile(
+ join(__dirname, '..', '__testfixtures__/fixture5.input.ts'),
+ 'utf-8'
+ );
+ const OUTPUT = await readFile(
+ join(__dirname, '..', '__testfixtures__/fixture5.output.ts'),
+ 'utf-8'
+ );
+
+ const fileInfo: FileInfo = {
+ path: 'index.ts',
+ source: INPUT,
+ };
+
+ const actualOutput = transform(fileInfo, buildApi('tsx'), {
+ quote: 'single',
+ });
+
+ assert.deepEqual(
+ actualOutput?.replace(/\W/gm, ''),
+ OUTPUT.replace(/\W/gm, '')
+ );
+ });
+
+ it('should not replace any.useContext() with use', async () => {
+ const INPUT = await readFile(
+ join(__dirname, '..', '__testfixtures__/fixture6.input.ts'),
+ 'utf-8'
+ );
+ const OUTPUT = await readFile(
+ join(__dirname, '..', '__testfixtures__/fixture6.output.ts'),
+ 'utf-8'
+ );
+
+ const fileInfo: FileInfo = {
+ path: 'index.ts',
+ source: INPUT,
+ };
+
+ const actualOutput = transform(fileInfo, buildApi('tsx'), {
+ quote: 'single',
+ });
+
+ assert.deepEqual(
+ actualOutput?.replace(/\W/gm, ''),
+ OUTPUT.replace(/\W/gm, '')
+ );
+ });
+ });
+});
diff --git a/codemods/use-context-hook/package.json b/codemods/use-context-hook/package.json
new file mode 100644
index 0000000000000..8faca62580bef
--- /dev/null
+++ b/codemods/use-context-hook/package.json
@@ -0,0 +1,17 @@
+{
+ "name": "use-context-hook",
+ "version": "1.0.0",
+ "dependencies": {},
+ "devDependencies": {
+ "@types/node": "^20.12.3",
+ "@types/jscodeshift": "^0.11.10",
+ "jscodeshift": "^0.15.1",
+ "typescript": "^5.2.2"
+ },
+ "files": [
+ "./README.md",
+ "./.codemodrc.json",
+ "./dist/index.cjs"
+ ],
+ "type": "module"
+}
diff --git a/codemods/use-context-hook/src/index.ts b/codemods/use-context-hook/src/index.ts
new file mode 100644
index 0000000000000..b905e2df1f93c
--- /dev/null
+++ b/codemods/use-context-hook/src/index.ts
@@ -0,0 +1,67 @@
+import type {API, FileInfo, Options} from 'jscodeshift';
+
+export default function transform(
+ file: FileInfo,
+ api: API,
+ options?: Options
+): string | undefined {
+ const j = api.jscodeshift;
+ const root = j(file.source);
+
+ // Get default import from react
+ const defaultReactImport =
+ root
+ .find(j.ImportDeclaration, {
+ source: {value: 'react'},
+ specifiers: [{type: 'ImportDefaultSpecifier'}],
+ })
+ .paths()
+ .at(0)
+ ?.node.specifiers?.at(0)?.local?.name ?? 'React';
+
+ // For usages like `import React from 'react'; React.useContext(ThemeContext)`
+ root
+ .find(j.MemberExpression, {
+ object: {name: defaultReactImport},
+ property: {name: 'useContext'},
+ })
+ .forEach(path => {
+ const identifierPath = j(path)
+ .find(j.Identifier, {name: 'useContext'})
+ .paths()
+ .at(0);
+
+ const newIdentifier = j.identifier.from({name: 'use'});
+
+ identifierPath?.replace(newIdentifier);
+ });
+
+ // Get useContext import name
+ const useContextImport = root
+ .find(j.ImportDeclaration, {
+ source: {value: 'react'},
+ specifiers: [{type: 'ImportSpecifier', imported: {name: 'useContext'}}],
+ })
+ .paths()
+ .at(0)
+ ?.node.specifiers?.at(0)?.local?.name;
+
+ if (useContextImport) {
+ // For usages like `import { useContext } from 'react'; useContext(ThemeContext)`
+ root.find(j.Identifier, {name: useContextImport}).forEach(path => {
+ // If parent is a member expression, we don't want that change, we handle React.useContext separately
+ if (path.parentPath.node.type === 'MemberExpression') {
+ return;
+ }
+
+ // In all other cases, replace usages of imported useContext with use
+ if (path.node.type === 'Identifier') {
+ const newIdentifier = j.identifier.from({name: 'use'});
+
+ path.replace(newIdentifier);
+ }
+ });
+ }
+
+ return root.toSource();
+}
diff --git a/codemods/use-context-hook/tsconfig.json b/codemods/use-context-hook/tsconfig.json
new file mode 100644
index 0000000000000..5717e5c332cf3
--- /dev/null
+++ b/codemods/use-context-hook/tsconfig.json
@@ -0,0 +1,25 @@
+{
+ "compilerOptions": {
+ "outDir": "./dist",
+ "esModuleInterop": true,
+ "forceConsistentCasingInFileNames": true,
+ "isolatedModules": true,
+ "module": "NodeNext",
+ "skipLibCheck": true,
+ "strict": true,
+ "target": "ES6",
+ "allowJs": true,
+ "allowImportingTsExtensions": true,
+ "noEmit": true
+ },
+ "include": [
+ "./src/**/*.ts",
+ "./src/**/*.js",
+ "./__tests__/**/*.ts",
+ "./__tests__/**/*.js"
+ ],
+ "exclude": ["node_modules", "./dist/**/*"],
+ "ts-node": {
+ "transpileOnly": true
+ }
+}
\ No newline at end of file
diff --git a/package.json b/package.json
index 785bbfe63113b..a309819d6b4a4 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,8 @@
{
"private": true,
"workspaces": [
- "packages/*"
+ "packages/*",
+ "codemods/*"
],
"devDependencies": {
"@babel/cli": "^7.10.5",
@@ -35,11 +36,14 @@
"@babel/plugin-transform-template-literals": "^7.10.5",
"@babel/preset-flow": "^7.10.4",
"@babel/preset-react": "^7.23.3",
+ "@babel/preset-typescript": "^7.24.1",
"@babel/traverse": "^7.11.0",
"@rollup/plugin-babel": "^6.0.3",
"@rollup/plugin-commonjs": "^24.0.1",
"@rollup/plugin-node-resolve": "^15.0.1",
"@rollup/plugin-replace": "^5.0.2",
+ "@types/jest": "^29.5.12",
+ "@types/node": "^20.12.3",
"abortcontroller-polyfill": "^1.7.5",
"art": "0.10.1",
"babel-plugin-syntax-trailing-function-commas": "^6.5.0",
diff --git a/scripts/jest/config.codemods.js b/scripts/jest/config.codemods.js
new file mode 100644
index 0000000000000..fcdf99392ae01
--- /dev/null
+++ b/scripts/jest/config.codemods.js
@@ -0,0 +1,14 @@
+'use strict';
+
+module.exports = {
+ globalSetup: require.resolve('./setupGlobal.js'),
+ modulePathIgnorePatterns: [
+ '/packages',
+ ],
+ testMatch: ['**/__tests__/**'],
+ moduleFileExtensions: ['js', 'ts'],
+ rootDir: process.cwd(),
+ roots: ['/codemods'],
+ testEnvironment: 'node',
+ testRunner: 'jest-circus/runner',
+};
diff --git a/scripts/jest/jest-cli.js b/scripts/jest/jest-cli.js
index 22098a190561e..1596c2a384fa0 100644
--- a/scripts/jest/jest-cli.js
+++ b/scripts/jest/jest-cli.js
@@ -10,6 +10,7 @@ const semver = require('semver');
const ossConfig = './scripts/jest/config.source.js';
const wwwConfig = './scripts/jest/config.source-www.js';
const devToolsConfig = './scripts/jest/config.build-devtools.js';
+const codemodsConfig = './scripts/jest/config.codemods.js';
// TODO: These configs are separate but should be rolled into the configs above
// so that the CLI can provide them as options for any of the configs.
@@ -38,7 +39,7 @@ const argv = yargs
requiresArg: true,
type: 'string',
default: 'default',
- choices: ['default', 'devtools'],
+ choices: ['default', 'devtools', 'codemods'],
},
releaseChannel: {
alias: 'r',
@@ -269,7 +270,9 @@ function validateOptions() {
function getCommandArgs() {
// Add the correct Jest config.
const args = ['./scripts/jest/jest.js', '--config'];
- if (argv.project === 'devtools') {
+ if (argv.project === 'codemods') {
+ args.push(codemodsConfig);
+ } else if (argv.project === 'devtools') {
args.push(devToolsConfig);
} else if (argv.build) {
args.push(buildConfig);
diff --git a/yarn.lock b/yarn.lock
index 8670efde1275b..bc1da8844b76d 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -10,6 +10,14 @@
"@jridgewell/gen-mapping" "^0.1.0"
"@jridgewell/trace-mapping" "^0.3.9"
+"@ampproject/remapping@^2.2.0":
+ version "2.3.0"
+ resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.3.0.tgz#ed441b6fa600072520ce18b43d2c8cc8caecc7f4"
+ integrity sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==
+ dependencies:
+ "@jridgewell/gen-mapping" "^0.3.5"
+ "@jridgewell/trace-mapping" "^0.3.24"
+
"@babel/cli@^7.10.5":
version "7.10.5"
resolved "https://registry.yarnpkg.com/@babel/cli/-/cli-7.10.5.tgz#57df2987c8cf89d0fc7d4b157ec59d7619f1b77a"
@@ -54,6 +62,14 @@
dependencies:
"@babel/highlight" "^7.18.6"
+"@babel/code-frame@^7.23.5", "@babel/code-frame@^7.24.1", "@babel/code-frame@^7.24.2":
+ version "7.24.2"
+ resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.24.2.tgz#718b4b19841809a58b29b68cde80bc5e1aa6d9ae"
+ integrity sha512-y5+tLQyV8pg3fsiln67BVLD1P13Eg4lh5RW9mF0zUuvLrv9uIQ4MCL+CRT+FTsBlBjcIan6PGsLcBN0m3ClUyQ==
+ dependencies:
+ "@babel/highlight" "^7.24.2"
+ picocolors "^1.0.0"
+
"@babel/code-frame@^7.8.3":
version "7.8.3"
resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.8.3.tgz#33e25903d7481181534e12ec0a25f16b6fcf419e"
@@ -80,6 +96,11 @@
resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.20.14.tgz#4106fc8b755f3e3ee0a0a7c27dde5de1d2b2baf8"
integrity sha512-0YpKHD6ImkWMEINCyDAD0HLLUH/lPCefG8ld9it8DJB2wnApraKuhgYTvTY1z7UFIfBTGy5LwncZ+5HWWGbhFw==
+"@babel/compat-data@^7.23.5":
+ version "7.24.1"
+ resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.24.1.tgz#31c1f66435f2a9c329bb5716a6d6186c516c3742"
+ integrity sha512-Pc65opHDliVpRHuKfzI+gSA4zcgr65O4cl64fFJIWEEh8JoHIHh0Oez1Eo8Arz8zq/JhgKodQaxEwUPRtZylVA==
+
"@babel/core@^7.0.0", "@babel/core@^7.11.6", "@babel/core@^7.12.3":
version "7.20.12"
resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.20.12.tgz#7930db57443c6714ad216953d1356dac0eb8496d"
@@ -144,6 +165,27 @@
semver "^6.3.0"
source-map "^0.5.0"
+"@babel/core@^7.23.0":
+ version "7.24.3"
+ resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.24.3.tgz#568864247ea10fbd4eff04dda1e05f9e2ea985c3"
+ integrity sha512-5FcvN1JHw2sHJChotgx8Ek0lyuh4kCKelgMTTqhYJJtloNvUfpAFMeNQUtdlIaktwrSV9LtCdqwk48wL2wBacQ==
+ dependencies:
+ "@ampproject/remapping" "^2.2.0"
+ "@babel/code-frame" "^7.24.2"
+ "@babel/generator" "^7.24.1"
+ "@babel/helper-compilation-targets" "^7.23.6"
+ "@babel/helper-module-transforms" "^7.23.3"
+ "@babel/helpers" "^7.24.1"
+ "@babel/parser" "^7.24.1"
+ "@babel/template" "^7.24.0"
+ "@babel/traverse" "^7.24.1"
+ "@babel/types" "^7.24.0"
+ convert-source-map "^2.0.0"
+ debug "^4.1.0"
+ gensync "^1.0.0-beta.2"
+ json5 "^2.2.3"
+ semver "^6.3.1"
+
"@babel/eslint-parser@^7.11.4":
version "7.11.4"
resolved "https://registry.yarnpkg.com/@babel/eslint-parser/-/eslint-parser-7.11.4.tgz#f79bac69088097a8418f5c67fc462c89a72c2f48"
@@ -189,6 +231,16 @@
"@jridgewell/gen-mapping" "^0.3.2"
jsesc "^2.5.1"
+"@babel/generator@^7.24.1":
+ version "7.24.1"
+ resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.24.1.tgz#e67e06f68568a4ebf194d1c6014235344f0476d0"
+ integrity sha512-DfCRfZsBcrPEHUfuBMgbJ1Ut01Y/itOs+hY2nFLgqsqXd52/iSiVq5TITtUasIUgm+IIKdY2/1I7auiQOEeC9A==
+ dependencies:
+ "@babel/types" "^7.24.0"
+ "@jridgewell/gen-mapping" "^0.3.5"
+ "@jridgewell/trace-mapping" "^0.3.25"
+ jsesc "^2.5.1"
+
"@babel/generator@^7.8.3":
version "7.8.3"
resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.8.3.tgz#0e22c005b0a94c1c74eafe19ef78ce53a4d45c03"
@@ -284,6 +336,17 @@
lru-cache "^5.1.1"
semver "^6.3.0"
+"@babel/helper-compilation-targets@^7.23.6":
+ version "7.23.6"
+ resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.23.6.tgz#4d79069b16cbcf1461289eccfbbd81501ae39991"
+ integrity sha512-9JB548GZoQVmzrFgp8o7KxdgkTGm6xs9DW0o/Pim72UDjzr5ObUQ6ZzYPqA+g9OTS2bBQoctLJrky0RDCAWRgQ==
+ dependencies:
+ "@babel/compat-data" "^7.23.5"
+ "@babel/helper-validator-option" "^7.23.5"
+ browserslist "^4.22.2"
+ lru-cache "^5.1.1"
+ semver "^6.3.1"
+
"@babel/helper-create-class-features-plugin@^7.10.4":
version "7.10.5"
resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.10.5.tgz#9f61446ba80e8240b0a5c85c6fdac8459d6f259d"
@@ -322,6 +385,21 @@
"@babel/helper-skip-transparent-expression-wrappers" "^7.20.0"
"@babel/helper-split-export-declaration" "^7.18.6"
+"@babel/helper-create-class-features-plugin@^7.24.1":
+ version "7.24.1"
+ resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.24.1.tgz#db58bf57137b623b916e24874ab7188d93d7f68f"
+ integrity sha512-1yJa9dX9g//V6fDebXoEfEsxkZHk3Hcbm+zLhyu6qVgYFLvmTALTeV+jNU9e5RnYtioBrGEOdoI2joMSNQ/+aA==
+ dependencies:
+ "@babel/helper-annotate-as-pure" "^7.22.5"
+ "@babel/helper-environment-visitor" "^7.22.20"
+ "@babel/helper-function-name" "^7.23.0"
+ "@babel/helper-member-expression-to-functions" "^7.23.0"
+ "@babel/helper-optimise-call-expression" "^7.22.5"
+ "@babel/helper-replace-supers" "^7.24.1"
+ "@babel/helper-skip-transparent-expression-wrappers" "^7.22.5"
+ "@babel/helper-split-export-declaration" "^7.22.6"
+ semver "^6.3.1"
+
"@babel/helper-create-regexp-features-plugin@^7.10.4":
version "7.10.4"
resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.10.4.tgz#fdd60d88524659a0b6959c0579925e425714f3b8"
@@ -345,6 +423,11 @@
resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz#0c0cee9b35d2ca190478756865bb3528422f51be"
integrity sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==
+"@babel/helper-environment-visitor@^7.22.20":
+ version "7.22.20"
+ resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz#96159db61d34a29dba454c959f5ae4a649ba9167"
+ integrity sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==
+
"@babel/helper-explode-assignable-expression@^7.10.4":
version "7.10.4"
resolved "https://registry.yarnpkg.com/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.10.4.tgz#40a1cd917bff1288f699a94a75b37a1a2dbd8c7c"
@@ -379,6 +462,14 @@
"@babel/template" "^7.18.10"
"@babel/types" "^7.19.0"
+"@babel/helper-function-name@^7.23.0":
+ version "7.23.0"
+ resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz#1f9a3cdbd5b2698a670c30d2735f9af95ed52759"
+ integrity sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==
+ dependencies:
+ "@babel/template" "^7.22.15"
+ "@babel/types" "^7.23.0"
+
"@babel/helper-function-name@^7.8.3":
version "7.8.3"
resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.8.3.tgz#eeeb665a01b1f11068e9fb86ad56a1cb1a824cca"
@@ -430,6 +521,13 @@
dependencies:
"@babel/types" "^7.18.6"
+"@babel/helper-hoist-variables@^7.22.5":
+ version "7.22.5"
+ resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz#c01a007dac05c085914e8fb652b339db50d823bb"
+ integrity sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==
+ dependencies:
+ "@babel/types" "^7.22.5"
+
"@babel/helper-member-expression-to-functions@^7.10.4", "@babel/helper-member-expression-to-functions@^7.10.5":
version "7.11.0"
resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.11.0.tgz#ae69c83d84ee82f4b42f96e2a09410935a8f26df"
@@ -451,6 +549,13 @@
dependencies:
"@babel/types" "^7.20.7"
+"@babel/helper-member-expression-to-functions@^7.23.0":
+ version "7.23.0"
+ resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.23.0.tgz#9263e88cc5e41d39ec18c9a3e0eced59a3e7d366"
+ integrity sha512-6gfrPwh7OuT6gZyJZvd6WbTfrqAo7vm4xCzAXOusKqq/vWdKXphTpj5klHKNmRUU6/QRGlBsyU9mAIPaWHlqJA==
+ dependencies:
+ "@babel/types" "^7.23.0"
+
"@babel/helper-module-imports@^7.10.4":
version "7.10.4"
resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.10.4.tgz#4c5c54be04bd31670a7382797d75b9fa2e5b5620"
@@ -520,6 +625,17 @@
"@babel/traverse" "^7.20.10"
"@babel/types" "^7.20.7"
+"@babel/helper-module-transforms@^7.23.3":
+ version "7.23.3"
+ resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.23.3.tgz#d7d12c3c5d30af5b3c0fcab2a6d5217773e2d0f1"
+ integrity sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ==
+ dependencies:
+ "@babel/helper-environment-visitor" "^7.22.20"
+ "@babel/helper-module-imports" "^7.22.15"
+ "@babel/helper-simple-access" "^7.22.5"
+ "@babel/helper-split-export-declaration" "^7.22.6"
+ "@babel/helper-validator-identifier" "^7.22.20"
+
"@babel/helper-optimise-call-expression@^7.10.4":
version "7.10.4"
resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.10.4.tgz#50dc96413d594f995a77905905b05893cd779673"
@@ -541,6 +657,13 @@
dependencies:
"@babel/types" "^7.18.6"
+"@babel/helper-optimise-call-expression@^7.22.5":
+ version "7.22.5"
+ resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.22.5.tgz#f21531a9ccbff644fdd156b4077c16ff0c3f609e"
+ integrity sha512-HBwaojN0xFRx4yIvpwGqxiV2tUfl7401jlok564NgB9EHS1y6QT17FmKWm4ztqjeVdXLuC4fSvHc5ePpQjoTbw==
+ dependencies:
+ "@babel/types" "^7.22.5"
+
"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.8.0", "@babel/helper-plugin-utils@^7.8.3":
version "7.8.3"
resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz#9ea293be19babc0f52ff8ca88b34c3611b208670"
@@ -566,6 +689,11 @@
resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz#dd7ee3735e8a313b9f7b05a773d892e88e6d7295"
integrity sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==
+"@babel/helper-plugin-utils@^7.24.0":
+ version "7.24.0"
+ resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.0.tgz#945681931a52f15ce879fd5b86ce2dae6d3d7f2a"
+ integrity sha512-9cUznXMG0+FxRuJfvL82QlTqIzhVW9sL0KjMPHhAOOvpQGL8QtdxnBKILjBqxlHyliz0yCa1G903ZXI/FuHy2w==
+
"@babel/helper-regex@^7.10.4":
version "7.10.5"
resolved "https://registry.yarnpkg.com/@babel/helper-regex/-/helper-regex-7.10.5.tgz#32dfbb79899073c415557053a19bd055aae50ae0"
@@ -616,6 +744,15 @@
"@babel/traverse" "^7.20.7"
"@babel/types" "^7.20.7"
+"@babel/helper-replace-supers@^7.24.1":
+ version "7.24.1"
+ resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.24.1.tgz#7085bd19d4a0b7ed8f405c1ed73ccb70f323abc1"
+ integrity sha512-QCR1UqC9BzG5vZl8BMicmZ28RuUBnHhAMddD8yHFHDRH9lLTZ9uUPehX8ctVPT8l0TKblJidqcgUUKGVrePleQ==
+ dependencies:
+ "@babel/helper-environment-visitor" "^7.22.20"
+ "@babel/helper-member-expression-to-functions" "^7.23.0"
+ "@babel/helper-optimise-call-expression" "^7.22.5"
+
"@babel/helper-simple-access@^7.10.4":
version "7.10.4"
resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.10.4.tgz#0f5ccda2945277a2a7a2d3a821e15395edcf3461"
@@ -638,6 +775,13 @@
dependencies:
"@babel/types" "^7.20.2"
+"@babel/helper-simple-access@^7.22.5":
+ version "7.22.5"
+ resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz#4938357dc7d782b80ed6dbb03a0fba3d22b1d5de"
+ integrity sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==
+ dependencies:
+ "@babel/types" "^7.22.5"
+
"@babel/helper-skip-transparent-expression-wrappers@^7.11.0":
version "7.11.0"
resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.11.0.tgz#eec162f112c2f58d3af0af125e3bb57665146729"
@@ -659,6 +803,13 @@
dependencies:
"@babel/types" "^7.20.0"
+"@babel/helper-skip-transparent-expression-wrappers@^7.22.5":
+ version "7.22.5"
+ resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.22.5.tgz#007f15240b5751c537c40e77abb4e89eeaaa8847"
+ integrity sha512-tK14r66JZKiC43p8Ki33yLBVJKlQDFoA8GYN67lWCDCqoL6EMMSuM9b+Iff2jHaM/RRFYl7K+iiru7hbRqNx8Q==
+ dependencies:
+ "@babel/types" "^7.22.5"
+
"@babel/helper-split-export-declaration@^7.10.4", "@babel/helper-split-export-declaration@^7.11.0":
version "7.11.0"
resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.11.0.tgz#f8a491244acf6a676158ac42072911ba83ad099f"
@@ -680,6 +831,13 @@
dependencies:
"@babel/types" "^7.18.6"
+"@babel/helper-split-export-declaration@^7.22.6":
+ version "7.22.6"
+ resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz#322c61b7310c0997fe4c323955667f18fcefb91c"
+ integrity sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==
+ dependencies:
+ "@babel/types" "^7.22.5"
+
"@babel/helper-split-export-declaration@^7.8.3":
version "7.8.3"
resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.8.3.tgz#31a9f30070f91368a7182cf05f831781065fc7a9"
@@ -727,7 +885,7 @@
resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz#bf0d2b5a509b1f336099e4ff36e1a63aa5db4db8"
integrity sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw==
-"@babel/helper-validator-option@^7.22.15":
+"@babel/helper-validator-option@^7.22.15", "@babel/helper-validator-option@^7.23.5":
version "7.23.5"
resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.23.5.tgz#907a3fbd4523426285365d1206c423c4c5520307"
integrity sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw==
@@ -769,6 +927,15 @@
"@babel/traverse" "^7.20.13"
"@babel/types" "^7.20.7"
+"@babel/helpers@^7.24.1":
+ version "7.24.1"
+ resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.24.1.tgz#183e44714b9eba36c3038e442516587b1e0a1a94"
+ integrity sha512-BpU09QqEe6ZCHuIHFphEFgvNSrubve1FtyMton26ekZ85gRGi6LrTF7zArARp2YvyFxloeiRmtSCq5sjh1WqIg==
+ dependencies:
+ "@babel/template" "^7.24.0"
+ "@babel/traverse" "^7.24.1"
+ "@babel/types" "^7.24.0"
+
"@babel/highlight@^7.10.4", "@babel/highlight@^7.18.6":
version "7.18.6"
resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.18.6.tgz#81158601e93e2563795adcbfbdf5d64be3f2ecdf"
@@ -796,6 +963,16 @@
chalk "^2.0.0"
js-tokens "^4.0.0"
+"@babel/highlight@^7.24.2":
+ version "7.24.2"
+ resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.24.2.tgz#3f539503efc83d3c59080a10e6634306e0370d26"
+ integrity sha512-Yac1ao4flkTxTteCDZLEvdxg2fZfz1v8M4QpaGypq/WPDqg3ijHYbDfs+LG5hvzSoqaSZ9/Z9lKSP3CjZjv+pA==
+ dependencies:
+ "@babel/helper-validator-identifier" "^7.22.20"
+ chalk "^2.4.2"
+ js-tokens "^4.0.0"
+ picocolors "^1.0.0"
+
"@babel/node@^7.14.7":
version "7.14.7"
resolved "https://registry.yarnpkg.com/@babel/node/-/node-7.14.7.tgz#0090e83e726027ea682240718ca39e4b625b15ad"
@@ -838,6 +1015,11 @@
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.16.4.tgz#d5f92f57cf2c74ffe9b37981c0e72fee7311372e"
integrity sha512-6V0qdPUaiVHH3RtZeLIsc+6pDhbYzHR8ogA8w+f+Wc77DuXto19g2QUwveINoS34Uw+W8/hQDGJCx+i4n7xcng==
+"@babel/parser@^7.23.0", "@babel/parser@^7.24.0", "@babel/parser@^7.24.1":
+ version "7.24.1"
+ resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.24.1.tgz#1e416d3627393fab1cb5b0f2f1796a100ae9133a"
+ integrity sha512-Zo9c7N3xdOIQrNip7Lc9wvRPzlRtovHVE4lkz8WEDr7uYh/GMQhSiIgFxGIArRHYdJE5kxtZjAf8rT0xhdLCzg==
+
"@babel/plugin-external-helpers@^7.10.4":
version "7.10.4"
resolved "https://registry.yarnpkg.com/@babel/plugin-external-helpers/-/plugin-external-helpers-7.10.4.tgz#40d38e8e48a1fa3766ab43496253266ca26783ce"
@@ -1095,6 +1277,13 @@
dependencies:
"@babel/helper-plugin-utils" "^7.10.4"
+"@babel/plugin-syntax-flow@^7.24.1":
+ version "7.24.1"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.24.1.tgz#875c25e3428d7896c87589765fc8b9d32f24bd8d"
+ integrity sha512-sxi2kLTI5DeW5vDtMUsk4mTPwvlUDbjOnoWayhynCwrw4QXRld4QEYwqzY8JmQXaJUtgUuCIurtSRH5sn4c7mA==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.24.0"
+
"@babel/plugin-syntax-import-meta@^7.10.4", "@babel/plugin-syntax-import-meta@^7.8.3":
version "7.10.4"
resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz#ee601348c370fa334d2207be158777496521fd51"
@@ -1130,6 +1319,13 @@
dependencies:
"@babel/helper-plugin-utils" "^7.22.5"
+"@babel/plugin-syntax-jsx@^7.24.1":
+ version "7.24.1"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.24.1.tgz#3f6ca04b8c841811dbc3c5c5f837934e0d626c10"
+ integrity sha512-2eCtxZXf+kbkMIsXS4poTvT4Yu5rXiRa+9xGVT56raghjmBTKMpFNc9R4IDiB4emao9eO22Ox7CxuJG7BgExqA==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.24.0"
+
"@babel/plugin-syntax-logical-assignment-operators@^7.10.4", "@babel/plugin-syntax-logical-assignment-operators@^7.8.3":
version "7.10.4"
resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz#ca91ef46303530448b906652bac2e9fe9941f699"
@@ -1207,6 +1403,13 @@
dependencies:
"@babel/helper-plugin-utils" "^7.14.5"
+"@babel/plugin-syntax-typescript@^7.24.1":
+ version "7.24.1"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.24.1.tgz#b3bcc51f396d15f3591683f90239de143c076844"
+ integrity sha512-Yhnmvy5HZEnHUty6i++gcfH1/l68AHnItFHnaCv6hn9dNh0hQvvQJsxpi4BMBFN5DLeHBuucT/0DgzXif/OyRw==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.24.0"
+
"@babel/plugin-syntax-typescript@^7.7.2":
version "7.20.0"
resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.20.0.tgz#4e9a0cfc769c85689b77a2e642d24e9f697fc8c7"
@@ -1265,6 +1468,14 @@
dependencies:
"@babel/helper-plugin-utils" "^7.10.4"
+"@babel/plugin-transform-class-properties@^7.22.5":
+ version "7.24.1"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.24.1.tgz#bcbf1aef6ba6085cfddec9fc8d58871cf011fc29"
+ integrity sha512-OMLCXi0NqvJfORTaPQBwqLXHhb93wkBKZ4aNwMl6WtehO7ar+cmp+89iPEQPqxAnxsOKTaMcs3POz3rKayJ72g==
+ dependencies:
+ "@babel/helper-create-class-features-plugin" "^7.24.1"
+ "@babel/helper-plugin-utils" "^7.24.0"
+
"@babel/plugin-transform-classes@^7.0.0":
version "7.20.7"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.20.7.tgz#f438216f094f6bb31dc266ebfab8ff05aecad073"
@@ -1362,6 +1573,14 @@
"@babel/helper-plugin-utils" "^7.10.4"
"@babel/plugin-syntax-flow" "^7.10.4"
+"@babel/plugin-transform-flow-strip-types@^7.24.1":
+ version "7.24.1"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.24.1.tgz#fa8d0a146506ea195da1671d38eed459242b2dcc"
+ integrity sha512-iIYPIWt3dUmUKKE10s3W+jsQ3icFkw0JyRVyY1B7G4yK/nngAOHLVx8xlhA6b/Jzl/Y0nis8gjqhqKtRDQqHWQ==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.24.0"
+ "@babel/plugin-syntax-flow" "^7.24.1"
+
"@babel/plugin-transform-for-of@^7.0.0":
version "7.18.8"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.18.8.tgz#6ef8a50b244eb6a0bdbad0c7c61877e4e30097c1"
@@ -1459,6 +1678,15 @@
"@babel/helper-simple-access" "^7.16.0"
babel-plugin-dynamic-import-node "^2.3.3"
+"@babel/plugin-transform-modules-commonjs@^7.23.0", "@babel/plugin-transform-modules-commonjs@^7.24.1":
+ version "7.24.1"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.24.1.tgz#e71ba1d0d69e049a22bf90b3867e263823d3f1b9"
+ integrity sha512-szog8fFTUxBfw0b98gEWPaEqF42ZUD/T3bkynW/wtgx2p/XCP55WEsb+VosKceRSd6njipdZvNogqdtI4Q0chw==
+ dependencies:
+ "@babel/helper-module-transforms" "^7.23.3"
+ "@babel/helper-plugin-utils" "^7.24.0"
+ "@babel/helper-simple-access" "^7.22.5"
+
"@babel/plugin-transform-modules-systemjs@^7.10.4":
version "7.10.5"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.10.5.tgz#6270099c854066681bae9e05f87e1b9cadbe8c85"
@@ -1491,6 +1719,14 @@
dependencies:
"@babel/helper-plugin-utils" "^7.10.4"
+"@babel/plugin-transform-nullish-coalescing-operator@^7.22.11":
+ version "7.24.1"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.24.1.tgz#0cd494bb97cb07d428bd651632cb9d4140513988"
+ integrity sha512-iQ+caew8wRrhCikO5DrUYx0mrmdhkaELgFa+7baMcVuhxIkN7oxt06CZ51D65ugIb1UWRQ8oQe+HXAVM6qHFjw==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.24.0"
+ "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3"
+
"@babel/plugin-transform-object-super@^7.0.0":
version "7.18.6"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.18.6.tgz#fb3c6ccdd15939b6ff7939944b51971ddc35912c"
@@ -1507,6 +1743,15 @@
"@babel/helper-plugin-utils" "^7.10.4"
"@babel/helper-replace-supers" "^7.10.4"
+"@babel/plugin-transform-optional-chaining@^7.23.0":
+ version "7.24.1"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.24.1.tgz#26e588acbedce1ab3519ac40cc748e380c5291e6"
+ integrity sha512-n03wmDt+987qXwAgcBlnUUivrZBPZ8z1plL0YvgQalLm+ZE5BMhGm94jhxXtA1wzv1Cu2aaOv1BM9vbVttrzSg==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.24.0"
+ "@babel/helper-skip-transparent-expression-wrappers" "^7.22.5"
+ "@babel/plugin-syntax-optional-chaining" "^7.8.3"
+
"@babel/plugin-transform-parameters@^7.0.0", "@babel/plugin-transform-parameters@^7.20.7":
version "7.20.7"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.20.7.tgz#0ee349e9d1bc96e78e3b37a7af423a4078a7083f"
@@ -1522,6 +1767,14 @@
"@babel/helper-get-function-arity" "^7.10.4"
"@babel/helper-plugin-utils" "^7.10.4"
+"@babel/plugin-transform-private-methods@^7.22.5":
+ version "7.24.1"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.24.1.tgz#a0faa1ae87eff077e1e47a5ec81c3aef383dc15a"
+ integrity sha512-tGvisebwBO5em4PaYNqt4fkw56K2VALsAbAakY0FjTYqJp7gfdrgr7YX76Or8/cpik0W6+tj3rZ0uHU9Oil4tw==
+ dependencies:
+ "@babel/helper-create-class-features-plugin" "^7.24.1"
+ "@babel/helper-plugin-utils" "^7.24.0"
+
"@babel/plugin-transform-property-literals@^7.0.0":
version "7.18.6"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.18.6.tgz#e22498903a483448e94e032e9bbb9c5ccbfc93a3"
@@ -1720,6 +1973,16 @@
"@babel/helper-plugin-utils" "^7.14.5"
"@babel/plugin-syntax-typescript" "^7.16.0"
+"@babel/plugin-transform-typescript@^7.24.1":
+ version "7.24.1"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.24.1.tgz#5c05e28bb76c7dfe7d6c5bed9951324fd2d3ab07"
+ integrity sha512-liYSESjX2fZ7JyBFkYG78nfvHlMKE6IpNdTVnxmlYUR+j5ZLsitFbaAE+eJSK2zPPkNWNw4mXL51rQ8WrvdK0w==
+ dependencies:
+ "@babel/helper-annotate-as-pure" "^7.22.5"
+ "@babel/helper-create-class-features-plugin" "^7.24.1"
+ "@babel/helper-plugin-utils" "^7.24.0"
+ "@babel/plugin-syntax-typescript" "^7.24.1"
+
"@babel/plugin-transform-unicode-escapes@^7.10.4":
version "7.10.4"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.10.4.tgz#feae523391c7651ddac115dae0a9d06857892007"
@@ -1825,6 +2088,15 @@
"@babel/helper-plugin-utils" "^7.10.4"
"@babel/plugin-transform-flow-strip-types" "^7.10.4"
+"@babel/preset-flow@^7.22.15":
+ version "7.24.1"
+ resolved "https://registry.yarnpkg.com/@babel/preset-flow/-/preset-flow-7.24.1.tgz#da7196c20c2d7dd4e98cfd8b192fe53b5eb6f0bb"
+ integrity sha512-sWCV2G9pcqZf+JHyv/RyqEIpFypxdCSxWIxQjpdaQxenNog7cN1pr76hg8u0Fz8Qgg0H4ETkGcJnXL8d4j0PPA==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.24.0"
+ "@babel/helper-validator-option" "^7.23.5"
+ "@babel/plugin-transform-flow-strip-types" "^7.24.1"
+
"@babel/preset-modules@^0.1.3":
version "0.1.3"
resolved "https://registry.yarnpkg.com/@babel/preset-modules/-/preset-modules-0.1.3.tgz#13242b53b5ef8c883c3cf7dddd55b36ce80fbc72"
@@ -1870,6 +2142,17 @@
"@babel/helper-validator-option" "^7.14.5"
"@babel/plugin-transform-typescript" "^7.16.0"
+"@babel/preset-typescript@^7.23.0", "@babel/preset-typescript@^7.24.1":
+ version "7.24.1"
+ resolved "https://registry.yarnpkg.com/@babel/preset-typescript/-/preset-typescript-7.24.1.tgz#89bdf13a3149a17b3b2a2c9c62547f06db8845ec"
+ integrity sha512-1DBaMmRDpuYQBPWD8Pf/WEwCrtgRHxsZnP4mIy9G/X+hFfbI47Q2G4t1Paakld84+qsk2fSsUPMKg71jkoOOaQ==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.24.0"
+ "@babel/helper-validator-option" "^7.23.5"
+ "@babel/plugin-syntax-jsx" "^7.24.1"
+ "@babel/plugin-transform-modules-commonjs" "^7.24.1"
+ "@babel/plugin-transform-typescript" "^7.24.1"
+
"@babel/register@^7.14.5":
version "7.14.5"
resolved "https://registry.yarnpkg.com/@babel/register/-/register-7.14.5.tgz#d0eac615065d9c2f1995842f85d6e56c345f3233"
@@ -1881,6 +2164,17 @@
pirates "^4.0.0"
source-map-support "^0.5.16"
+"@babel/register@^7.22.15":
+ version "7.23.7"
+ resolved "https://registry.yarnpkg.com/@babel/register/-/register-7.23.7.tgz#485a5e7951939d21304cae4af1719fdb887bc038"
+ integrity sha512-EjJeB6+kvpk+Y5DAkEAmbOBEFkh9OASx0huoEkqYTFxAZHzOAX2Oh5uwAUuL2rUddqfM0SA+KPXV2TbzoZ2kvQ==
+ dependencies:
+ clone-deep "^4.0.1"
+ find-cache-dir "^2.0.0"
+ make-dir "^2.1.0"
+ pirates "^4.0.6"
+ source-map-support "^0.5.16"
+
"@babel/runtime-corejs2@^7.2.0":
version "7.8.3"
resolved "https://registry.yarnpkg.com/@babel/runtime-corejs2/-/runtime-corejs2-7.8.3.tgz#b62a61e0c41a90d2d91181fda6de21cecd3a9734"
@@ -1944,6 +2238,15 @@
"@babel/parser" "^7.20.7"
"@babel/types" "^7.20.7"
+"@babel/template@^7.22.15", "@babel/template@^7.24.0":
+ version "7.24.0"
+ resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.24.0.tgz#c6a524aa93a4a05d66aaf31654258fae69d87d50"
+ integrity sha512-Bkf2q8lMB0AFpX0NFEqSbx1OkTHf0f+0j82mkw+ZpzBnkk7e9Ql0891vlfgi+kHwOk8tQjiQHpqh4LaSa0fKEA==
+ dependencies:
+ "@babel/code-frame" "^7.23.5"
+ "@babel/parser" "^7.24.0"
+ "@babel/types" "^7.24.0"
+
"@babel/template@^7.8.3":
version "7.8.3"
resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.8.3.tgz#e02ad04fe262a657809327f578056ca15fd4d1b8"
@@ -2029,6 +2332,22 @@
debug "^4.1.0"
globals "^11.1.0"
+"@babel/traverse@^7.24.1":
+ version "7.24.1"
+ resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.24.1.tgz#d65c36ac9dd17282175d1e4a3c49d5b7988f530c"
+ integrity sha512-xuU6o9m68KeqZbQuDt2TcKSxUw/mrsvavlEqQ1leZ/B+C9tk6E4sRWy97WaXgvq5E+nU3cXMxv3WKOCanVMCmQ==
+ dependencies:
+ "@babel/code-frame" "^7.24.1"
+ "@babel/generator" "^7.24.1"
+ "@babel/helper-environment-visitor" "^7.22.20"
+ "@babel/helper-function-name" "^7.23.0"
+ "@babel/helper-hoist-variables" "^7.22.5"
+ "@babel/helper-split-export-declaration" "^7.22.6"
+ "@babel/parser" "^7.24.1"
+ "@babel/types" "^7.24.0"
+ debug "^4.3.1"
+ globals "^11.1.0"
+
"@babel/types@^7.0.0", "@babel/types@^7.8.3":
version "7.8.3"
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.8.3.tgz#5a383dffa5416db1b73dedffd311ffd0788fb31c"
@@ -2081,6 +2400,15 @@
"@babel/helper-validator-identifier" "^7.22.20"
to-fast-properties "^2.0.0"
+"@babel/types@^7.23.0", "@babel/types@^7.24.0":
+ version "7.24.0"
+ resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.24.0.tgz#3b951f435a92e7333eba05b7566fd297960ea1bf"
+ integrity sha512-+j7a5c253RfKh8iABBhywc8NSfP5LURe7Uh4qpsh6jc+aLJguvmIUBdjSdEMQv2bENrCR5MfRdjGo7vzS/ob7w==
+ dependencies:
+ "@babel/helper-string-parser" "^7.23.4"
+ "@babel/helper-validator-identifier" "^7.22.20"
+ to-fast-properties "^2.0.0"
+
"@bcoe/v8-coverage@^0.2.3":
version "0.2.3"
resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39"
@@ -2280,6 +2608,13 @@
dependencies:
jest-get-type "^29.4.2"
+"@jest/expect-utils@^29.7.0":
+ version "29.7.0"
+ resolved "https://registry.yarnpkg.com/@jest/expect-utils/-/expect-utils-29.7.0.tgz#023efe5d26a8a70f21677d0a1afc0f0a44e3a1c6"
+ integrity sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==
+ dependencies:
+ jest-get-type "^29.6.3"
+
"@jest/expect@^29.4.2":
version "29.4.2"
resolved "https://registry.yarnpkg.com/@jest/expect/-/expect-29.4.2.tgz#2d4a6a41b29380957c5094de19259f87f194578b"
@@ -2354,6 +2689,13 @@
dependencies:
"@sinclair/typebox" "^0.25.16"
+"@jest/schemas@^29.6.3":
+ version "29.6.3"
+ resolved "https://registry.yarnpkg.com/@jest/schemas/-/schemas-29.6.3.tgz#430b5ce8a4e0044a7e3819663305a7b3091c8e03"
+ integrity sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==
+ dependencies:
+ "@sinclair/typebox" "^0.27.8"
+
"@jest/source-map@^29.4.2":
version "29.4.2"
resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-29.4.2.tgz#f9815d59e25cd3d6828e41489cd239271018d153"
@@ -2427,6 +2769,18 @@
"@types/yargs" "^17.0.8"
chalk "^4.0.0"
+"@jest/types@^29.6.3":
+ version "29.6.3"
+ resolved "https://registry.yarnpkg.com/@jest/types/-/types-29.6.3.tgz#1131f8cf634e7e84c5e77bab12f052af585fba59"
+ integrity sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==
+ dependencies:
+ "@jest/schemas" "^29.6.3"
+ "@types/istanbul-lib-coverage" "^2.0.0"
+ "@types/istanbul-reports" "^3.0.0"
+ "@types/node" "*"
+ "@types/yargs" "^17.0.8"
+ chalk "^4.0.0"
+
"@jridgewell/gen-mapping@^0.1.0":
version "0.1.1"
resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz#e5d2e450306a9491e3bd77e323e38d7aff315996"
@@ -2453,16 +2807,35 @@
"@jridgewell/sourcemap-codec" "^1.4.10"
"@jridgewell/trace-mapping" "^0.3.9"
+"@jridgewell/gen-mapping@^0.3.5":
+ version "0.3.5"
+ resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz#dcce6aff74bdf6dad1a95802b69b04a2fcb1fb36"
+ integrity sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==
+ dependencies:
+ "@jridgewell/set-array" "^1.2.1"
+ "@jridgewell/sourcemap-codec" "^1.4.10"
+ "@jridgewell/trace-mapping" "^0.3.24"
+
"@jridgewell/resolve-uri@3.1.0":
version "3.1.0"
resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz#2203b118c157721addfe69d47b70465463066d78"
integrity sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==
+"@jridgewell/resolve-uri@^3.1.0":
+ version "3.1.2"
+ resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz#7a0ee601f60f99a20c7c7c5ff0c80388c1189bd6"
+ integrity sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==
+
"@jridgewell/set-array@^1.0.0", "@jridgewell/set-array@^1.0.1":
version "1.1.2"
resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.2.tgz#7c6cf998d6d20b914c0a55a91ae928ff25965e72"
integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==
+"@jridgewell/set-array@^1.2.1":
+ version "1.2.1"
+ resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.2.1.tgz#558fb6472ed16a4c850b889530e6b36438c49280"
+ integrity sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==
+
"@jridgewell/source-map@^0.3.2":
version "0.3.3"
resolved "https://registry.yarnpkg.com/@jridgewell/source-map/-/source-map-0.3.3.tgz#8108265659d4c33e72ffe14e33d6cc5eb59f2fda"
@@ -2476,7 +2849,7 @@
resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz#add4c98d341472a289190b424efbdb096991bb24"
integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==
-"@jridgewell/sourcemap-codec@^1.4.15":
+"@jridgewell/sourcemap-codec@^1.4.14", "@jridgewell/sourcemap-codec@^1.4.15":
version "1.4.15"
resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32"
integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==
@@ -2497,6 +2870,14 @@
"@jridgewell/resolve-uri" "3.1.0"
"@jridgewell/sourcemap-codec" "1.4.14"
+"@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.25":
+ version "0.3.25"
+ resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz#15f190e98895f3fc23276ee14bc76b675c2e50f0"
+ integrity sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==
+ dependencies:
+ "@jridgewell/resolve-uri" "^3.1.0"
+ "@jridgewell/sourcemap-codec" "^1.4.14"
+
"@leichtgewicht/ip-codec@^2.0.1":
version "2.0.4"
resolved "https://registry.yarnpkg.com/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz#b2ac626d6cb9c8718ab459166d4bb405b8ffa78b"
@@ -2838,6 +3219,11 @@
resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.25.21.tgz#763b05a4b472c93a8db29b2c3e359d55b29ce272"
integrity sha512-gFukHN4t8K4+wVC+ECqeqwzBDeFeTzBXroBTqE6vcWrQGbEUpHO7LYdG0f4xnvYq4VOEwITSlHlp0JBAIFMS/g==
+"@sinclair/typebox@^0.27.8":
+ version "0.27.8"
+ resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.27.8.tgz#6667fac16c436b5434a387a34dedb013198f6e6e"
+ integrity sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==
+
"@sindresorhus/is@^0.14.0":
version "0.14.0"
resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.14.0.tgz#9fb3a3cf3132328151f353de4632e01e52102bea"
@@ -3063,6 +3449,22 @@
dependencies:
"@types/istanbul-lib-report" "*"
+"@types/jest@^29.5.12":
+ version "29.5.12"
+ resolved "https://registry.yarnpkg.com/@types/jest/-/jest-29.5.12.tgz#7f7dc6eb4cf246d2474ed78744b05d06ce025544"
+ integrity sha512-eDC8bTvT/QhYdxJAulQikueigY5AsdBRH2yDKW3yveW7svY3+DzN84/2NUgkw10RTiJbWqZrTtoGVdYlvFJdLw==
+ dependencies:
+ expect "^29.0.0"
+ pretty-format "^29.0.0"
+
+"@types/jscodeshift@^0.11.10":
+ version "0.11.11"
+ resolved "https://registry.yarnpkg.com/@types/jscodeshift/-/jscodeshift-0.11.11.tgz#30d7c986f372cd63c670017371da8fbced2b7acf"
+ integrity sha512-d7CAfFGOupj5qCDqMODXxNz2/NwCv/Lha78ZFbnr6qpk3K98iSB8I+ig9ERE2+EeYML352VMRsjPyOpeA+04eQ==
+ dependencies:
+ ast-types "^0.14.1"
+ recast "^0.20.3"
+
"@types/jsdom@^20.0.0":
version "20.0.1"
resolved "https://registry.yarnpkg.com/@types/jsdom/-/jsdom-20.0.1.tgz#07c14bc19bd2f918c1929541cdaacae894744808"
@@ -3124,6 +3526,13 @@
resolved "https://registry.yarnpkg.com/@types/node/-/node-16.18.14.tgz#5465ce598486a703caddbefe8603f8a2cffa3461"
integrity sha512-wvzClDGQXOCVNU4APPopC2KtMYukaF1MN/W3xAmslx22Z4/IF1/izDMekuyoUlwfnDHYCIZGaj7jMwnJKBTxKw==
+"@types/node@^20.12.3":
+ version "20.12.3"
+ resolved "https://registry.yarnpkg.com/@types/node/-/node-20.12.3.tgz#d6658c2c7776c1cad93534bb45428195ed840c65"
+ integrity sha512-sD+ia2ubTeWrOu+YMF+MTAB7E+O7qsMqAbMfW7DG3K1URwhZ5hN1pLlRVGbf4wDFzSfikL05M17EyorS86jShw==
+ dependencies:
+ undici-types "~5.26.4"
+
"@types/prettier@^1.0.0 || ^2.0.0 || ^3.0.0":
version "3.0.0"
resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-3.0.0.tgz#e9bc8160230d3a461dab5c5b41cceef1ef723057"
@@ -4220,6 +4629,20 @@ assign-symbols@^1.0.0:
resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367"
integrity sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=
+ast-types@0.14.2, ast-types@^0.14.1:
+ version "0.14.2"
+ resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.14.2.tgz#600b882df8583e3cd4f2df5fa20fa83759d4bdfd"
+ integrity sha512-O0yuUDnZeQDL+ncNGlJ78BiO4jnYI3bvMsD5prT0/nsgijG/LpNBIr63gTjVTNsiGkgQhiyCShTgxt8oXOrklA==
+ dependencies:
+ tslib "^2.0.1"
+
+ast-types@^0.16.1:
+ version "0.16.1"
+ resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.16.1.tgz#7a9da1617c9081bc121faafe91711b4c8bb81da2"
+ integrity sha512-6t10qk83GOG8p0vKmaCr8eiilZwO171AvbROMtvvNiwrTly62t+7XkA8RdIIVbpMhCASAsxgAzdRSwh6nw/5Dg==
+ dependencies:
+ tslib "^2.0.1"
+
astral-regex@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-1.0.0.tgz#6c8c3fb827dd43ee3918f27b82782ab7658a6fd9"
@@ -4305,7 +4728,7 @@ babel-code-frame@^6.26.0:
esutils "^2.0.2"
js-tokens "^3.0.2"
-babel-core@^7.0.0-bridge:
+babel-core@^7.0.0-bridge, babel-core@^7.0.0-bridge.0:
version "7.0.0-bridge.0"
resolved "https://registry.yarnpkg.com/babel-core/-/babel-core-7.0.0-bridge.0.tgz#95a492ddd90f9b4e9a4a1da14eb335b87b634ece"
integrity sha512-poPX9mZH/5CSanm50Q+1toVci6pv5KSRv/5TWCwtzQS5XEwn40BcCrgIeMFWP9CKKIniKXNxoIOnOq4VVlGXhg==
@@ -4944,6 +5367,16 @@ browserslist@^4.17.5:
node-releases "^2.0.1"
picocolors "^1.0.0"
+browserslist@^4.22.2:
+ version "4.23.0"
+ resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.23.0.tgz#8f3acc2bbe73af7213399430890f86c63a5674ab"
+ integrity sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==
+ dependencies:
+ caniuse-lite "^1.0.30001587"
+ electron-to-chromium "^1.4.668"
+ node-releases "^2.0.14"
+ update-browserslist-db "^1.0.13"
+
browserslist@^4.8.3:
version "4.8.5"
resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.8.5.tgz#691af4e327ac877b25e7a3f7ee869c4ef36cdea3"
@@ -5176,6 +5609,11 @@ caniuse-lite@^1.0.30001449:
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001450.tgz#022225b91200589196b814b51b1bbe45144cf74f"
integrity sha512-qMBmvmQmFXaSxexkjjfMvD5rnDL0+m+dUMZKoDYsGG8iZN29RuYh9eRoMvKsT6uMAWlyUUGDEQGJJYjzCIO9ew==
+caniuse-lite@^1.0.30001587:
+ version "1.0.30001605"
+ resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001605.tgz#ca12d7330dd8bcb784557eb9aa64f0037870d9d6"
+ integrity sha512-nXwGlFWo34uliI9z3n6Qc0wZaf7zaZWA1CPZ169La5mV3I/gem7bst0vr5XQH5TJXZIMfDeZyOrZnSlVzKxxHQ==
+
capture-stack-trace@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/capture-stack-trace/-/capture-stack-trace-1.0.1.tgz#a6c0bbe1f38f3aa0b92238ecb6ff42c344d4135d"
@@ -5204,7 +5642,7 @@ chalk@4.1.0, chalk@^4.0.0:
ansi-styles "^4.1.0"
supports-color "^7.1.0"
-chalk@4.x:
+chalk@4.x, chalk@^4.1.2:
version "4.1.2"
resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01"
integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==
@@ -6432,6 +6870,11 @@ diff-sequences@^29.4.2:
resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-29.4.2.tgz#711fe6bd8a5869fe2539cee4a5152425ff671fda"
integrity sha512-R6P0Y6PrsH3n4hUXxL3nns0rbRk6Q33js3ygJBeEpbzLzgcNuJ61+u0RXasFpTKISw99TxUzFnumSnRLsjhLaw==
+diff-sequences@^29.6.3:
+ version "29.6.3"
+ resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-29.6.3.tgz#4deaf894d11407c51efc8418012f9e70b84ea921"
+ integrity sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==
+
diff@5.1.0:
version "5.1.0"
resolved "https://registry.yarnpkg.com/diff/-/diff-5.1.0.tgz#bc52d298c5ea8df9194800224445ed43ffc87e40"
@@ -6670,6 +7113,11 @@ electron-to-chromium@^1.4.284:
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.284.tgz#61046d1e4cab3a25238f6bf7413795270f125592"
integrity sha512-M8WEXFuKXMYMVr45fo8mq0wUrrJHheiKZf6BArTKk9ZBYCKJEOU5H8cdWgDT+qCVZf7Na4lVUaZsA+h6uA9+PA==
+electron-to-chromium@^1.4.668:
+ version "1.4.724"
+ resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.724.tgz#e0a86fe4d3d0e05a4d7b032549d79608078f830d"
+ integrity sha512-RTRvkmRkGhNBPPpdrgtDKvmOEYTrPlXDfc0J/Nfq5s29tEahAwhiX4mmhNzj6febWMleulxVYPh7QwCSL/EldA==
+
electron@^23.1.2:
version "23.1.2"
resolved "https://registry.yarnpkg.com/electron/-/electron-23.1.2.tgz#f03e361c94f8dd2407963b5461d19aadea335316"
@@ -7201,7 +7649,7 @@ espree@^7.3.0, espree@^7.3.1:
acorn-jsx "^5.3.1"
eslint-visitor-keys "^1.3.0"
-esprima@4.0.1, esprima@^4.0.0, esprima@^4.0.1:
+esprima@4.0.1, esprima@^4.0.0, esprima@^4.0.1, esprima@~4.0.0:
version "4.0.1"
resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71"
integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==
@@ -7400,6 +7848,17 @@ expect@=27.2.5:
jest-message-util "^27.2.5"
jest-regex-util "^27.0.6"
+expect@^29.0.0:
+ version "29.7.0"
+ resolved "https://registry.yarnpkg.com/expect/-/expect-29.7.0.tgz#578874590dcb3214514084c08115d8aee61e11bc"
+ integrity sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==
+ dependencies:
+ "@jest/expect-utils" "^29.7.0"
+ jest-get-type "^29.6.3"
+ jest-matcher-utils "^29.7.0"
+ jest-message-util "^29.7.0"
+ jest-util "^29.7.0"
+
expect@^29.4.2:
version "29.4.2"
resolved "https://registry.yarnpkg.com/expect/-/expect-29.4.2.tgz#2ae34eb88de797c64a1541ad0f1e2ea8a7a7b492"
@@ -7962,6 +8421,11 @@ flow-bin@^0.232.0:
resolved "https://registry.yarnpkg.com/flow-bin/-/flow-bin-0.232.0.tgz#80587406cbb3a74577151ad27c6058b2a468c215"
integrity sha512-7uOycTN+Ys2nYRJRig5S2yN41ZokW7bC4K1GC4nCDa/3FAZLP5/mQbee6UjxFBP9MC4yUYi17bdFTFzCH8bHeg==
+flow-parser@0.*:
+ version "0.232.0"
+ resolved "https://registry.yarnpkg.com/flow-parser/-/flow-parser-0.232.0.tgz#db93a660e7017bd366290944c3328ca506ca7d2b"
+ integrity sha512-U8vcKyYdM+Kb0tPzfPJ5JyPMU0uXKwHxp0L6BcEc+wBlbTW9qRhOqV5DeGXclgclVvtqQNGEG8Strj/b6c/IxA==
+
flow-remove-types@^2.232.0:
version "2.232.0"
resolved "https://registry.yarnpkg.com/flow-remove-types/-/flow-remove-types-2.232.0.tgz#a4333fee2524b57220791a130955f48e0b107f1a"
@@ -9894,6 +10358,16 @@ jest-diff@^29.4.2:
jest-get-type "^29.4.2"
pretty-format "^29.4.2"
+jest-diff@^29.7.0:
+ version "29.7.0"
+ resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-29.7.0.tgz#017934a66ebb7ecf6f205e84699be10afd70458a"
+ integrity sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==
+ dependencies:
+ chalk "^4.0.0"
+ diff-sequences "^29.6.3"
+ jest-get-type "^29.6.3"
+ pretty-format "^29.7.0"
+
jest-docblock@^29.4.2:
version "29.4.2"
resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-29.4.2.tgz#c78a95eedf9a24c0a6cc16cf2abdc4b8b0f2531b"
@@ -9956,6 +10430,11 @@ jest-get-type@^29.4.2:
resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-29.4.2.tgz#7cb63f154bca8d8f57364d01614477d466fa43fe"
integrity sha512-vERN30V5i2N6lqlFu4ljdTqQAgrkTFMC9xaIIfOPYBw04pufjXRty5RuXBiB1d72tGbURa/UgoiHB90ruOSivg==
+jest-get-type@^29.6.3:
+ version "29.6.3"
+ resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-29.6.3.tgz#36f499fdcea197c1045a127319c0481723908fd1"
+ integrity sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==
+
jest-haste-map@^29.4.2:
version "29.4.2"
resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-29.4.2.tgz#9112df3f5121e643f1b2dcbaa86ab11b0b90b49a"
@@ -10013,6 +10492,16 @@ jest-matcher-utils@^29.4.2:
jest-get-type "^29.4.2"
pretty-format "^29.4.2"
+jest-matcher-utils@^29.7.0:
+ version "29.7.0"
+ resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz#ae8fec79ff249fd592ce80e3ee474e83a6c44f12"
+ integrity sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==
+ dependencies:
+ chalk "^4.0.0"
+ jest-diff "^29.7.0"
+ jest-get-type "^29.6.3"
+ pretty-format "^29.7.0"
+
jest-message-util@^27.2.5:
version "27.3.1"
resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-27.3.1.tgz#f7c25688ad3410ab10bcb862bcfe3152345c6436"
@@ -10043,6 +10532,21 @@ jest-message-util@^29.4.2:
slash "^3.0.0"
stack-utils "^2.0.3"
+jest-message-util@^29.7.0:
+ version "29.7.0"
+ resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-29.7.0.tgz#8bc392e204e95dfe7564abbe72a404e28e51f7f3"
+ integrity sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==
+ dependencies:
+ "@babel/code-frame" "^7.12.13"
+ "@jest/types" "^29.6.3"
+ "@types/stack-utils" "^2.0.0"
+ chalk "^4.0.0"
+ graceful-fs "^4.2.9"
+ micromatch "^4.0.4"
+ pretty-format "^29.7.0"
+ slash "^3.0.0"
+ stack-utils "^2.0.3"
+
jest-mock@^29.4.2:
version "29.4.2"
resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-29.4.2.tgz#e1054be66fb3e975d26d4528fcde6979e4759de8"
@@ -10193,6 +10697,18 @@ jest-util@^29.4.2:
graceful-fs "^4.2.9"
picomatch "^2.2.3"
+jest-util@^29.7.0:
+ version "29.7.0"
+ resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-29.7.0.tgz#23c2b62bfb22be82b44de98055802ff3710fc0bc"
+ integrity sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==
+ dependencies:
+ "@jest/types" "^29.6.3"
+ "@types/node" "*"
+ chalk "^4.0.0"
+ ci-info "^3.2.0"
+ graceful-fs "^4.2.9"
+ picomatch "^2.2.3"
+
jest-validate@^29.4.2:
version "29.4.2"
resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-29.4.2.tgz#3b3f8c4910ab9a3442d2512e2175df6b3f77b915"
@@ -10311,6 +10827,32 @@ jsc-safe-url@^0.2.4:
resolved "https://registry.yarnpkg.com/jsc-safe-url/-/jsc-safe-url-0.2.4.tgz#141c14fbb43791e88d5dc64e85a374575a83477a"
integrity sha512-0wM3YBWtYePOjfyXQH5MWQ8H7sdk5EXSwZvmSLKk2RboVQ2Bu239jycHDz5J/8Blf3K0Qnoy2b6xD+z10MFB+Q==
+jscodeshift@^0.15.1:
+ version "0.15.2"
+ resolved "https://registry.yarnpkg.com/jscodeshift/-/jscodeshift-0.15.2.tgz#145563860360b4819a558c75c545f39683e5a0be"
+ integrity sha512-FquR7Okgmc4Sd0aEDwqho3rEiKR3BdvuG9jfdHjLJ6JQoWSMpavug3AoIfnfWhxFlf+5pzQh8qjqz0DWFrNQzA==
+ dependencies:
+ "@babel/core" "^7.23.0"
+ "@babel/parser" "^7.23.0"
+ "@babel/plugin-transform-class-properties" "^7.22.5"
+ "@babel/plugin-transform-modules-commonjs" "^7.23.0"
+ "@babel/plugin-transform-nullish-coalescing-operator" "^7.22.11"
+ "@babel/plugin-transform-optional-chaining" "^7.23.0"
+ "@babel/plugin-transform-private-methods" "^7.22.5"
+ "@babel/preset-flow" "^7.22.15"
+ "@babel/preset-typescript" "^7.23.0"
+ "@babel/register" "^7.22.15"
+ babel-core "^7.0.0-bridge.0"
+ chalk "^4.1.2"
+ flow-parser "0.*"
+ graceful-fs "^4.2.4"
+ micromatch "^4.0.4"
+ neo-async "^2.5.0"
+ node-dir "^0.1.17"
+ recast "^0.23.3"
+ temp "^0.8.4"
+ write-file-atomic "^2.3.0"
+
jsdom@^20.0.0:
version "20.0.3"
resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-20.0.3.tgz#886a41ba1d4726f67a8858028c99489fed6ad4db"
@@ -10433,7 +10975,7 @@ json5@^2.1.2, json5@^2.1.3:
dependencies:
minimist "^1.2.5"
-json5@^2.2.2:
+json5@^2.2.2, json5@^2.2.3:
version "2.2.3"
resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283"
integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==
@@ -11242,6 +11784,13 @@ minimalistic-assert@^1.0.0:
dependencies:
brace-expansion "^1.1.7"
+minimatch@^3.0.2:
+ version "3.1.2"
+ resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b"
+ integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==
+ dependencies:
+ brace-expansion "^1.1.7"
+
minimatch@^5.0.1:
version "5.1.6"
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.1.6.tgz#1cfcb8cf5522ea69952cd2af95ae09477f122a96"
@@ -11437,6 +11986,13 @@ node-cleanup@^2.1.2:
resolved "https://registry.yarnpkg.com/node-cleanup/-/node-cleanup-2.1.2.tgz#7ac19abd297e09a7f72a71545d951b517e4dde2c"
integrity sha1-esGavSl+Caf3KnFUXZUbUX5N3iw=
+node-dir@^0.1.17:
+ version "0.1.17"
+ resolved "https://registry.yarnpkg.com/node-dir/-/node-dir-0.1.17.tgz#5f5665d93351335caabef8f1c554516cf5f1e4e5"
+ integrity sha512-tmPX422rYgofd4epzrNoOXiE8XFZYOcCq1vD7MAXCDO+O+zndlA2ztdKKMa+EeuBG5tHETpr4ml4RGgpqDCCAg==
+ dependencies:
+ minimatch "^3.0.2"
+
node-environment-flags@^1.0.5:
version "1.0.6"
resolved "https://registry.yarnpkg.com/node-environment-flags/-/node-environment-flags-1.0.6.tgz#a30ac13621f6f7d674260a54dede048c3982c088"
@@ -11544,6 +12100,11 @@ node-releases@^2.0.1:
resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.1.tgz#3d1d395f204f1f2f29a54358b9fb678765ad2fc5"
integrity sha512-CqyzN6z7Q6aMeF/ktcMVTzhAHCEpf8SOarwpzpf8pNBY2k5/oM34UHldUwp8VKI7uxct2HxSRdJjBaZeESzcxA==
+node-releases@^2.0.14:
+ version "2.0.14"
+ resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.14.tgz#2ffb053bceb8b2be8495ece1ab6ce600c4461b0b"
+ integrity sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==
+
node-releases@^2.0.8:
version "2.0.9"
resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.9.tgz#fe66405285382b0c4ac6bcfbfbe7e8a510650b4d"
@@ -12413,6 +12974,11 @@ pirates@^4.0.4:
resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.5.tgz#feec352ea5c3268fb23a37c702ab1699f35a5f3b"
integrity sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ==
+pirates@^4.0.6:
+ version "4.0.6"
+ resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.6.tgz#3018ae32ecfcff6c29ba2267cbf21166ac1f36b9"
+ integrity sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==
+
pixelmatch@^5.2.1:
version "5.2.1"
resolved "https://registry.yarnpkg.com/pixelmatch/-/pixelmatch-5.2.1.tgz#9e4e4f4aa59648208a31310306a5bed5522b0d65"
@@ -12623,6 +13189,15 @@ pretty-format@^27.2.5, pretty-format@^27.3.1:
ansi-styles "^5.0.0"
react-is "^17.0.1"
+pretty-format@^29.0.0, pretty-format@^29.7.0:
+ version "29.7.0"
+ resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-29.7.0.tgz#ca42c758310f365bfa71a0bda0a807160b776812"
+ integrity sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==
+ dependencies:
+ "@jest/schemas" "^29.6.3"
+ ansi-styles "^5.0.0"
+ react-is "^18.0.0"
+
pretty-format@^29.4.1:
version "29.4.1"
resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-29.4.1.tgz#0da99b532559097b8254298da7c75a0785b1751c"
@@ -13142,6 +13717,27 @@ readline-sync@^1.4.9:
resolved "https://registry.yarnpkg.com/readline-sync/-/readline-sync-1.4.10.tgz#41df7fbb4b6312d673011594145705bf56d8873b"
integrity sha512-gNva8/6UAe8QYepIQH/jQ2qn91Qj0B9sYjMBBs3QOB8F2CXcKgLxQaJRP76sWVRQt+QU+8fAkCbCvjjMFu7Ycw==
+recast@^0.20.3:
+ version "0.20.5"
+ resolved "https://registry.yarnpkg.com/recast/-/recast-0.20.5.tgz#8e2c6c96827a1b339c634dd232957d230553ceae"
+ integrity sha512-E5qICoPoNL4yU0H0NoBDntNB0Q5oMSNh9usFctYniLBluTthi3RsQVBXIJNbApOlvSwW/RGxIuokPcAc59J5fQ==
+ dependencies:
+ ast-types "0.14.2"
+ esprima "~4.0.0"
+ source-map "~0.6.1"
+ tslib "^2.0.1"
+
+recast@^0.23.3:
+ version "0.23.6"
+ resolved "https://registry.yarnpkg.com/recast/-/recast-0.23.6.tgz#198fba74f66143a30acc81929302d214ce4e3bfa"
+ integrity sha512-9FHoNjX1yjuesMwuthAmPKabxYQdOgihFYmT5ebXfYGBcnqXZf3WOVz+5foEZ8Y83P4ZY6yQD5GMmtV+pgCCAQ==
+ dependencies:
+ ast-types "^0.16.1"
+ esprima "~4.0.0"
+ source-map "~0.6.1"
+ tiny-invariant "^1.3.3"
+ tslib "^2.0.1"
+
rechoir@^0.6.2:
version "0.6.2"
resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.6.2.tgz#85204b54dba82d5742e28c96756ef43af50e3384"
@@ -13484,7 +14080,7 @@ reusify@^1.0.4:
resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76"
integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==
-rimraf@2.6.3:
+rimraf@2.6.3, rimraf@~2.6.2:
version "2.6.3"
resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab"
integrity sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==
@@ -13800,6 +14396,11 @@ semver@^6.0.0, semver@^6.2.0, semver@^6.3.0:
resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d"
integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==
+semver@^6.3.1:
+ version "6.3.1"
+ resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4"
+ integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==
+
semver@^7.1.1:
version "7.1.1"
resolved "https://registry.yarnpkg.com/semver/-/semver-7.1.1.tgz#29104598a197d6cbe4733eeecbe968f7b43a9667"
@@ -14459,7 +15060,7 @@ string-natural-compare@^3.0.1:
resolved "https://registry.yarnpkg.com/string-natural-compare/-/string-natural-compare-3.0.1.tgz#7a42d58474454963759e8e8b7ae63d71c1e7fdf4"
integrity sha512-n3sPwynL1nwKi3WJ6AIsClwBMa0zTi54fn2oLU6ndfTSIO05xaznjSf15PcBZU6FNWbmN5Q6cxT4V5hGvB4taw==
-"string-width-cjs@npm:string-width@^4.2.0":
+"string-width-cjs@npm:string-width@^4.2.0", string-width@^4.2.3:
version "4.2.3"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
@@ -14494,15 +15095,6 @@ string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0:
is-fullwidth-code-point "^3.0.0"
strip-ansi "^6.0.0"
-string-width@^4.2.3:
- version "4.2.3"
- resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
- integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
- dependencies:
- emoji-regex "^8.0.0"
- is-fullwidth-code-point "^3.0.0"
- strip-ansi "^6.0.1"
-
string-width@^5.0.1, string-width@^5.1.2:
version "5.1.2"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794"
@@ -14563,7 +15155,7 @@ string_decoder@~1.1.1:
dependencies:
safe-buffer "~5.1.0"
-"strip-ansi-cjs@npm:strip-ansi@^6.0.1":
+"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.1:
version "6.0.1"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
@@ -14598,13 +15190,6 @@ strip-ansi@^6.0.0:
dependencies:
ansi-regex "^5.0.0"
-strip-ansi@^6.0.1:
- version "6.0.1"
- resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
- integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
- dependencies:
- ansi-regex "^5.0.1"
-
strip-ansi@^7.0.1:
version "7.1.0"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45"
@@ -14877,6 +15462,13 @@ temp-dir@^1.0.0:
resolved "https://registry.yarnpkg.com/temp-dir/-/temp-dir-1.0.0.tgz#0a7c0ea26d3a39afa7e0ebea9c1fc0bc4daa011d"
integrity sha1-CnwOom06Oa+n4OvqnB/AvE2qAR0=
+temp@^0.8.4:
+ version "0.8.4"
+ resolved "https://registry.yarnpkg.com/temp/-/temp-0.8.4.tgz#8c97a33a4770072e0a05f919396c7665a7dd59f2"
+ integrity sha512-s0ZZzd0BzYv5tLSptZooSjK8oj6C+c19p7Vqta9+6NPOf7r+fxq0cJe6/oN4LTC79sy5NY8ucOJNgwsKCSbfqg==
+ dependencies:
+ rimraf "~2.6.2"
+
tempfile@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/tempfile/-/tempfile-2.0.0.tgz#6b0446856a9b1114d1856ffcbe509cccb0977265"
@@ -15004,6 +15596,11 @@ timers-browserify@^1.0.1:
dependencies:
process "~0.11.0"
+tiny-invariant@^1.3.3:
+ version "1.3.3"
+ resolved "https://registry.yarnpkg.com/tiny-invariant/-/tiny-invariant-1.3.3.tgz#46680b7a873a0d5d10005995eb90a70d74d60127"
+ integrity sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==
+
tiny-warning@^1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/tiny-warning/-/tiny-warning-1.0.3.tgz#94a30db453df4c643d0fd566060d60a875d84754"
@@ -15182,6 +15779,11 @@ tslib@^1.9.0:
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.11.2.tgz#9c79d83272c9a7aaf166f73915c9667ecdde3cc9"
integrity sha512-tTSkux6IGPnUGUd1XAZHcpu85MOkIl5zX49pO+jfsie3eP0B6pyhOlLXm3cAC6T7s+euSDDUUV+Acop5WmtkVg==
+tslib@^2.0.1:
+ version "2.6.2"
+ resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.2.tgz#703ac29425e7b37cd6fd456e92404d46d1f3e4ae"
+ integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==
+
tslib@^2.3.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.3.0.tgz#803b8cdab3e12ba581a4ca41c8839bbb0dacb09e"
@@ -15287,6 +15889,11 @@ typescript@^3.7.5:
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.7.5.tgz#0692e21f65fd4108b9330238aac11dd2e177a1ae"
integrity sha512-/P5lkRXkWHNAbcJIiHPfRoKqyd7bsyCma1hZNUGfn20qm64T6ZBlrzprymeu918H+mB/0rIg2gGK/BXkhhYgBw==
+typescript@^5.2.2:
+ version "5.4.3"
+ resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.4.3.tgz#5c6fedd4c87bee01cd7a528a30145521f8e0feff"
+ integrity sha512-KrPd3PKaCLr78MalgiwJnA25Nm8HAmdwN3mYUYZgG/wizIo9EainNVQI9/yDavtVFRN2h3k8uf3GLHuhDMgEHg==
+
ua-parser-js@^0.7.18, ua-parser-js@^0.7.9:
version "0.7.20"
resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.20.tgz#7527178b82f6a62a0f243d1f94fd30e3e3c21098"
@@ -15315,6 +15922,11 @@ unc-path-regex@^0.1.0, unc-path-regex@^0.1.2:
resolved "https://registry.yarnpkg.com/unc-path-regex/-/unc-path-regex-0.1.2.tgz#e73dd3d7b0d7c5ed86fbac6b0ae7d8c6a69d50fa"
integrity sha1-5z3T17DXxe2G+6xrCufYxqadUPo=
+undici-types@~5.26.4:
+ version "5.26.5"
+ resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617"
+ integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==
+
unicode-canonical-property-names-ecmascript@^1.0.4:
version "1.0.4"
resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz#2619800c4c825800efdd8343af7dd9933cbe2818"
@@ -15426,6 +16038,14 @@ update-browserslist-db@^1.0.10:
escalade "^3.1.1"
picocolors "^1.0.0"
+update-browserslist-db@^1.0.13:
+ version "1.0.13"
+ resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz#3c5e4f5c083661bd38ef64b6328c26ed6c8248c4"
+ integrity sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==
+ dependencies:
+ escalade "^3.1.1"
+ picocolors "^1.0.0"
+
update-notifier@4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/update-notifier/-/update-notifier-4.0.0.tgz#f344a6f8b03e00e31b323d632a0e632e9f0e0654"
@@ -16040,7 +16660,7 @@ workerize-loader@^2.0.2:
dependencies:
loader-utils "^2.0.0"
-"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0":
+"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0:
version "7.0.0"
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
@@ -16058,15 +16678,6 @@ wrap-ansi@^6.2.0:
string-width "^4.1.0"
strip-ansi "^6.0.0"
-wrap-ansi@^7.0.0:
- version "7.0.0"
- resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
- integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
- dependencies:
- ansi-styles "^4.0.0"
- string-width "^4.1.0"
- strip-ansi "^6.0.0"
-
wrap-ansi@^8.1.0:
version "8.1.0"
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214"
@@ -16081,7 +16692,7 @@ wrappy@1:
resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=
-write-file-atomic@^2.0.0:
+write-file-atomic@^2.0.0, write-file-atomic@^2.3.0:
version "2.4.3"
resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-2.4.3.tgz#1fd2e9ae1df3e75b8d8c367443c692d4ca81f481"
integrity sha512-GaETH5wwsX+GcnzhPgKcKjJ6M2Cq3/iZp1WyY/X1CSqrW+jVNM9Y7D8EC2sM4ZG/V8wZlSniJnCKWPmBYAucRQ==