Skip to content

Commit 8468017

Browse files
authored
feat: update-register codemod
update-register codemod
2 parents c51f9fd + 23879f0 commit 8468017

22 files changed

+696
-4
lines changed

.eslintrc.js

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,16 @@ module.exports = {
1111
},
1212
plugins: ['@typescript-eslint'],
1313
rules: {
14-
semi: ['error', 'always']
15-
}
14+
semi: ['error', 'always'],
15+
'comma-dangle': ['error', 'never'],
16+
'space-before-function-paren': ['error', 'never']
17+
},
18+
overrides: [
19+
{
20+
files: 'transforms/__testfixtures__/**',
21+
rules: {
22+
'no-undef': 'off'
23+
}
24+
}
25+
]
1626
};

.prettierrc.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
{
22
"singleQuote": true,
3-
"trailingComma": "all"
3+
"trailingComma": "none"
44
}

README.md

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,48 @@ This will start an interactive wizard, and then run the specified transform.
3434

3535
### Migrate from V6 to V7
3636

37+
#### `v7/update-register`
38+
39+
Update the `register` API inside a component which use `useForm` of React Hook Form. This transform is not applied if the component doesn't use `useForm`.
40+
41+
npx @hookform/codemod v7/update-register
42+
43+
<details>
44+
<summary>Examples</summary>
45+
46+
```diff
47+
- <input ref={register} name="example" />
48+
+ <input {...register('example')} />
49+
50+
- <input ref={register()} name="example" />
51+
+ <input {...register('example')} />
52+
53+
- <input ref={register()} name="example" />
54+
+ <input {...register('example')} />
55+
56+
- <input ref={register({ required: true })} name="example" />
57+
+ <input {...register('example', { required: true })} />
58+
59+
- <TextInput ref={register({ required: true })} name="example" />
60+
+ <TextInput {...register('example', { required: true })} />
61+
```
62+
63+
With a custom `register` name
64+
65+
```diff
66+
function MyForm() {
67+
const { register: customRegister } = useForm();
68+
69+
return (
70+
<form>
71+
- <input ref={customRegister} name="example" />
72+
+ <input {...customRegister('example')} />
73+
</form>
74+
);
75+
}
76+
```
77+
78+
</details>
3779

3880
## Backers
3981

bin/cli.ts

Lines changed: 198 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
1+
/**
2+
* Copyright 2015-present, Facebook, Inc.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*
7+
*/
8+
// Based on https://github.com/reactjs/react-codemod/blob/dd8671c9a470a2c342b221ec903c574cf31e9f57/bin/cli.js
9+
// @hookform/codemod optional-name-of-transform optional/path/to/src [...options]
10+
11+
const globby = require('globby');
12+
const inquirer = require('inquirer');
13+
const meow = require('meow');
14+
const path = require('path');
15+
const execa = require('execa');
16+
const chalk = require('chalk');
17+
const isGitClean = require('is-git-clean');
18+
19+
const transformerDirectory = path.join(__dirname, '../', 'transforms');
20+
const jscodeshiftExecutable = require.resolve('.bin/jscodeshift');
21+
22+
function checkGitStatus(force) {
23+
let clean = false;
24+
let errorMessage = 'Unable to determine if git directory is clean';
25+
try {
26+
clean = isGitClean.sync(process.cwd());
27+
errorMessage = 'Git directory is not clean';
28+
} catch (err) {
29+
if (err && err.stderr && err.stderr.indexOf('Not a git repository') >= 0) {
30+
clean = true;
31+
}
32+
}
33+
34+
if (!clean) {
35+
if (force) {
36+
console.log(`WARNING: ${errorMessage}. Forcibly continuing.`);
37+
} else {
38+
console.log('Thank you for using @hookform/codemod!');
39+
console.log(
40+
chalk.yellow(
41+
'\nBut before we continue, please stash or commit your git changes.'
42+
)
43+
);
44+
console.log(
45+
'\nYou may use the --force flag to override this safety check.'
46+
);
47+
process.exit(1);
48+
}
49+
}
50+
}
51+
52+
function runTransform({ files, flags, transformer }) {
53+
const transformerPath = path.join(transformerDirectory, `${transformer}.js`);
54+
55+
let args = [];
56+
57+
const { dry, print } = flags;
58+
59+
if (dry) {
60+
args.push('--dry');
61+
}
62+
if (print) {
63+
args.push('--print');
64+
}
65+
66+
args.push('--verbose=2');
67+
68+
args.push('--ignore-pattern=**/node_modules/**');
69+
70+
args.push('--extensions=tsx,ts,jsx,js');
71+
args.push('--parser=tsx');
72+
73+
args = args.concat(['--transform', transformerPath]);
74+
75+
if (flags.jscodeshift) {
76+
args = args.concat(flags.jscodeshift);
77+
}
78+
79+
args = args.concat(files);
80+
81+
console.log(`Executing command: jscodeshift ${args.join(' ')}`);
82+
83+
const result = execa.sync(jscodeshiftExecutable, args, {
84+
stdio: 'inherit',
85+
stripEof: false
86+
});
87+
88+
if (result.error) {
89+
throw result.error;
90+
}
91+
}
92+
93+
const TRANSFORMER_INQUIRER_CHOICES = [
94+
{
95+
name: 'v7/update-register: Transforms register api from v6 to v7',
96+
value: 'v7/update-register'
97+
}
98+
];
99+
100+
function expandFilePathsIfNeeded(filesBeforeExpansion) {
101+
const shouldExpandFiles = filesBeforeExpansion.some((file) =>
102+
file.includes('*')
103+
);
104+
return shouldExpandFiles
105+
? globby.sync(filesBeforeExpansion)
106+
: filesBeforeExpansion;
107+
}
108+
109+
function run() {
110+
const cli = meow(
111+
{
112+
description: 'Codemods for updating react-hook-form apps.',
113+
help: `
114+
Usage
115+
$ npx @hookform/codemod <transform> <path> <...options>
116+
transform One of the choices from https://github.com/react-hook-form/codemod/blob/master/README.md
117+
path Files or directory to transform. Can be a glob like src/**.js
118+
Options
119+
--force Bypass Git safety checks and forcibly run codemods
120+
--dry Dry run (no changes are made to files)
121+
--print Print transformed files to your terminal
122+
--jscodeshift (Advanced) Pass options directly to jscodeshift
123+
`
124+
},
125+
{
126+
boolean: ['force', 'dry', 'print', 'help'],
127+
string: ['_'],
128+
alias: {
129+
h: 'help'
130+
}
131+
}
132+
);
133+
134+
if (!cli.flags.dry) {
135+
checkGitStatus(cli.flags.force);
136+
}
137+
138+
if (
139+
cli.input[0] &&
140+
!TRANSFORMER_INQUIRER_CHOICES.find((x) => x.value === cli.input[0])
141+
) {
142+
console.error('Invalid transform choice, pick one of:');
143+
console.error(
144+
TRANSFORMER_INQUIRER_CHOICES.map((x) => '- ' + x.value).join('\n')
145+
);
146+
process.exit(1);
147+
}
148+
149+
inquirer
150+
.prompt([
151+
{
152+
type: 'input',
153+
name: 'files',
154+
message: 'On which files or directory should the codemods be applied?',
155+
when: !cli.input[1],
156+
default: '.',
157+
// validate: () =>
158+
filter: (files) => files.trim()
159+
},
160+
{
161+
type: 'list',
162+
name: 'transformer',
163+
message: 'Which transform would you like to apply?',
164+
when: !cli.input[0],
165+
pageSize: TRANSFORMER_INQUIRER_CHOICES.length,
166+
choices: TRANSFORMER_INQUIRER_CHOICES
167+
}
168+
])
169+
.then((answers) => {
170+
const { files, transformer } = answers;
171+
172+
const filesBeforeExpansion = cli.input[1] || files;
173+
const filesExpanded = expandFilePathsIfNeeded([filesBeforeExpansion]);
174+
175+
const selectedTransformer = cli.input[0] || transformer;
176+
177+
if (!filesExpanded.length) {
178+
console.log(
179+
`No files found matching ${filesBeforeExpansion.join(' ')}`
180+
);
181+
return null;
182+
}
183+
184+
return runTransform({
185+
files: filesExpanded,
186+
flags: cli.flags,
187+
transformer: selectedTransformer
188+
});
189+
});
190+
}
191+
192+
module.exports = {
193+
run: run,
194+
runTransform: runTransform,
195+
checkGitStatus: checkGitStatus,
196+
jscodeshiftExecutable: jscodeshiftExecutable,
197+
transformerDirectory: transformerDirectory
198+
};

bin/hookform-codemod.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
#!/usr/bin/env node
2+
3+
/**
4+
* Copyright 2015-present, Facebook, Inc.
5+
*
6+
* This source code is licensed under the MIT license found in the
7+
* LICENSE file in the root directory of this source tree.
8+
*
9+
*/
10+
// Based on https://github.com/reactjs/react-codemod/blob/dd8671c9a470a2c342b221ec903c574cf31e9f57/bin/react-codemod.js
11+
// @hookform/codemod optional-name-of-transform optional/path/to/src [...options]
12+
13+
require('./cli').run();
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { useForm } from 'react-hook-form';
2+
3+
const Form = () => {
4+
const { register } = useForm();
5+
6+
return (
7+
<form>
8+
<input {...register('example', { required: true })} />
9+
<input {...register('example-2')} />
10+
<input {...register('example-3')} />
11+
<input ref="useless-ref" name="example" />
12+
</form>
13+
);
14+
};
15+
16+
export default Form;
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { useForm } from 'react-hook-form';
2+
3+
const Form = () => {
4+
const { register } = useForm();
5+
6+
return (
7+
<form>
8+
<input {...register('example', { required: true })} />
9+
<input {...register('example-2')} />
10+
<input {...register('example-3')} />
11+
<input ref="useless-ref" name="example" />
12+
</form>
13+
);
14+
};
15+
16+
export default Form;
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { useForm as useRenamedForm } from 'react-hook-form';
2+
3+
const Form = () => {
4+
const { register: customRegister } = useRenamedForm();
5+
6+
return (
7+
<form>
8+
<input ref={customRegister} name="example" />
9+
</form>
10+
);
11+
};
12+
13+
export default Form;
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { useForm as useRenamedForm } from 'react-hook-form';
2+
3+
const Form = () => {
4+
const { register: customRegister } = useRenamedForm();
5+
6+
return (
7+
<form>
8+
<input {...customRegister('example')} />
9+
</form>
10+
);
11+
};
12+
13+
export default Form;
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import { useForm } from 'react-hook-form';
2+
3+
const Form = () => {
4+
const { register, handleSubmit } = useForm();
5+
6+
return (
7+
<form onSubmit={handleSubmit}>
8+
<InputContainer label="Email">
9+
<Input
10+
name="email"
11+
ref={register}
12+
autoComplete="username"
13+
/>
14+
</InputContainer>
15+
<InputContainer label="Password *">
16+
<Input
17+
name="password"
18+
ref={register({
19+
required: true,
20+
validate: (value) => !!value
21+
})}
22+
type="password"
23+
/>
24+
</InputContainer>
25+
</form>
26+
);
27+
};
28+
29+
export default Form;

0 commit comments

Comments
 (0)