Skip to content

Commit

Permalink
add replace-act codemod and update configs
Browse files Browse the repository at this point in the history
  • Loading branch information
r4zendev committed Apr 5, 2024
1 parent 15f05cc commit 0a30757
Show file tree
Hide file tree
Showing 22 changed files with 460 additions and 5 deletions.
3 changes: 3 additions & 0 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down
12 changes: 12 additions & 0 deletions codemods/replace-act-import/.codemodrc.json
Original file line number Diff line number Diff line change
@@ -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"]]
}
}
42 changes: 42 additions & 0 deletions codemods/replace-act-import/README.md
Original file line number Diff line number Diff line change
@@ -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();
```

Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
import {act} from 'react-dom/test-utils';
act();
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
import {act} from 'react';
act();
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
import ReactTestUtils from 'react-dom/test-utils';
ReactTestUtils.act();
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
import React from 'react';
React.act();
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
import * as ReactTestUtils from 'react-dom/test-utils';
ReactTestUtils.act();
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
import * as React from 'react';
React.act();
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
import {other} from 'react-dom/test-utils';
other.thing();
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
import {other} from 'react-dom/test-utils';
other.thing();
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import * as React from 'react';
import * as ReactTestUtils from 'react-dom/test-utils';

ReactTestUtils.act();
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import * as React from 'react';

React.act();
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import {FC} from 'react';
import {act} from 'react-dom/test-utils';
act();
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
import {FC, act} from 'react';
act();
174 changes: 174 additions & 0 deletions codemods/replace-act-import/__tests__/test.ts
Original file line number Diff line number Diff line change
@@ -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, '')
);
});
});
});
17 changes: 17 additions & 0 deletions codemods/replace-act-import/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"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"
}
Loading

0 comments on commit 0a30757

Please sign in to comment.