Skip to content

Commit 4d2bed5

Browse files
Merge pull request #57 from alexreardon/memoize-one@5.0.0
adding codemod for memoize one@5.0.0
2 parents f668c19 + ed794b2 commit 4d2bed5

File tree

4 files changed

+302
-0
lines changed

4 files changed

+302
-0
lines changed

community/memoize-one/5.0.0/motions/.gitkeep

Whitespace-only changes.
Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
1+
import { applyTransform } from '@codeshift/test-utils';
2+
import * as transformer from './transform';
3+
import prettier from 'prettier';
4+
5+
function format(source: string): string {
6+
return prettier
7+
.format(source, {
8+
parser: 'typescript',
9+
trailingComma: 'all',
10+
semi: true,
11+
tabWidth: 2,
12+
useTabs: false,
13+
singleQuote: true,
14+
printWidth: 100,
15+
})
16+
.trim();
17+
}
18+
19+
describe('memoize-one@5.0.0 transform', () => {
20+
it('should not touch usages that do not use a custom equality function', () => {
21+
const result = applyTransform(
22+
transformer,
23+
format(`
24+
import memoize from 'memoize-one';
25+
26+
function add(a: number, b: number) {
27+
return a + b;
28+
}
29+
30+
const memoized = memoize(add);
31+
`),
32+
{ parser: 'tsx' },
33+
);
34+
35+
expect(result).toEqual(
36+
format(`
37+
import memoize from 'memoize-one';
38+
39+
function add(a: number, b: number) {
40+
return a + b;
41+
}
42+
43+
const memoized = memoize(add);
44+
`),
45+
);
46+
});
47+
48+
it('should wrap inline equality arrow functions', () => {
49+
const result = applyTransform(
50+
transformer,
51+
format(`
52+
import memoize from 'memoize-one';
53+
54+
function add(a: number, b: number) {
55+
return a + b;
56+
}
57+
58+
const memoized = memoize(add, (a, b) => {
59+
return a === b;
60+
});
61+
`),
62+
{ parser: 'tsx' },
63+
);
64+
65+
expect(result).toEqual(
66+
format(`
67+
import memoize from 'memoize-one';
68+
69+
function add(a: number, b: number) {
70+
return a + b;
71+
}
72+
73+
const memoized = memoize(add, (newArgs, lastArgs) => {
74+
if (newArgs.length !== lastArgs.length) {
75+
return false;
76+
}
77+
78+
const __equalityFn = (a, b) => {
79+
return a === b;
80+
};
81+
82+
return newArgs.every((newArg, index) => __equalityFn(newArg, lastArgs[index]));
83+
});
84+
`),
85+
);
86+
});
87+
88+
it('should wrap inline equality function declarations', () => {
89+
const result = applyTransform(
90+
transformer,
91+
format(`
92+
import memoize from 'memoize-one';
93+
94+
function add(a: number, b: number) {
95+
return a + b;
96+
}
97+
98+
const memoized = memoize(add, function isEqual(a, b) {
99+
return a === b;
100+
});
101+
`),
102+
{ parser: 'tsx' },
103+
);
104+
105+
expect(result).toEqual(
106+
format(`
107+
import memoize from 'memoize-one';
108+
109+
function add(a: number, b: number) {
110+
return a + b;
111+
}
112+
113+
const memoized = memoize(add, (newArgs, lastArgs) => {
114+
if (newArgs.length !== lastArgs.length) {
115+
return false;
116+
}
117+
118+
const __equalityFn = function isEqual(a, b) {
119+
return a === b;
120+
}
121+
122+
return newArgs.every((newArg, index) => __equalityFn(newArg, lastArgs[index]));
123+
});
124+
`),
125+
);
126+
});
127+
128+
it('should wrap function identifiers', () => {
129+
const result = applyTransform(
130+
transformer,
131+
format(`
132+
import memoize from 'memoize-one';
133+
import isEqual from 'something';
134+
135+
function add(a: number, b: number) {
136+
return a + b;
137+
}
138+
139+
const memoized = memoize(add, isEqual);
140+
`),
141+
{ parser: 'tsx' },
142+
);
143+
144+
expect(result).toEqual(
145+
format(`
146+
import memoize from 'memoize-one';
147+
import isEqual from 'something';
148+
149+
function add(a: number, b: number) {
150+
return a + b;
151+
}
152+
153+
const memoized = memoize(add, (newArgs, lastArgs) => {
154+
if (newArgs.length !== lastArgs.length) {
155+
return false;
156+
}
157+
158+
const __equalityFn = isEqual;
159+
return newArgs.every((newArg, index) => __equalityFn(newArg, lastArgs[index]));
160+
});
161+
`),
162+
);
163+
});
164+
165+
it('should add a comment when an unsupported equality fn is encountered', () => {
166+
const result = applyTransform(
167+
transformer,
168+
format(`
169+
import memoize from 'memoize-one';
170+
171+
function add(a: number, b: number) {
172+
return a + b;
173+
}
174+
175+
const memoized = memoize(add, {});
176+
`),
177+
{ parser: 'tsx' },
178+
);
179+
180+
expect(result).toEqual(
181+
format(`
182+
/* TODO: (@codeshift) Unable to migrate memoize-one custom equality function.
183+
Expected a function or an identifier */
184+
import memoize from 'memoize-one';
185+
186+
function add(a: number, b: number) {
187+
return a + b;
188+
}
189+
190+
const memoized = memoize(add, {});
191+
`),
192+
);
193+
});
194+
});
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
import {
2+
hasImportDeclaration,
3+
getDefaultImportSpecifierName,
4+
insertCommentToStartOfFile,
5+
} from '@codeshift/utils';
6+
import { API, FileInfo, Options } from 'jscodeshift';
7+
8+
export default function transformer(
9+
file: FileInfo,
10+
{ jscodeshift: j }: API,
11+
options: Options,
12+
) {
13+
const source = j(file.source);
14+
15+
// Exit early if file is not importing memoize-one
16+
if (!hasImportDeclaration(j, source, 'memoize-one')) {
17+
return file.source;
18+
}
19+
20+
const importName = getDefaultImportSpecifierName(j, source, 'memoize-one');
21+
22+
source
23+
.find(j.CallExpression)
24+
// looking for calls to memoize-one
25+
.filter(
26+
call =>
27+
call.value.callee.type === 'Identifier' &&
28+
call.value.callee.name === importName,
29+
)
30+
.forEach(call => {
31+
const equalityFn = call.value.arguments[1];
32+
// we don't need to do anything for calls without an equality fn
33+
if (equalityFn == null) {
34+
return;
35+
}
36+
// We are going to wrap the existing customEqualityFn in our new one
37+
// 4.0.0 EqualityFn → (a, b) => boolean [called for each argument]
38+
// 5.0.0 EqualityFn → ([newArgs], [lastArgs]) => boolean [called once with all arguments]
39+
if (
40+
equalityFn.type === 'FunctionExpression' ||
41+
equalityFn.type === 'ArrowFunctionExpression' ||
42+
equalityFn.type === 'Identifier'
43+
) {
44+
const customEqualityFn = j.arrowFunctionExpression(
45+
[j.identifier('newArgs'), j.identifier('lastArgs')],
46+
// Exit early if the newArgs and lastArgs are different lengths
47+
j.blockStatement([
48+
j.ifStatement(
49+
j.binaryExpression(
50+
'!==',
51+
j.memberExpression(
52+
j.identifier('newArgs'),
53+
j.identifier('length'),
54+
),
55+
j.memberExpression(
56+
j.identifier('lastArgs'),
57+
j.identifier('length'),
58+
),
59+
),
60+
j.blockStatement([j.returnStatement(j.booleanLiteral(false))]),
61+
),
62+
// create a __equalityFn constant that points to the existing equality function
63+
j.variableDeclaration('const', [
64+
j.variableDeclarator(j.identifier('__equalityFn'), equalityFn),
65+
]),
66+
// Call the original equalityFn for each argument
67+
j.returnStatement(
68+
j.callExpression(
69+
j.memberExpression(
70+
j.identifier('newArgs'),
71+
j.identifier('every'),
72+
),
73+
[
74+
j.arrowFunctionExpression(
75+
[j.identifier('newArg'), j.identifier('index')],
76+
j.callExpression(j.identifier('__equalityFn'), [
77+
j.identifier('newArg'),
78+
j.memberExpression(
79+
j.identifier('lastArgs'),
80+
j.identifier('index'),
81+
// computed: lastArgs[index]
82+
true,
83+
),
84+
]),
85+
),
86+
],
87+
),
88+
),
89+
]),
90+
);
91+
92+
call.value.arguments[1] = customEqualityFn;
93+
return;
94+
}
95+
96+
insertCommentToStartOfFile(
97+
j,
98+
source,
99+
'Unable to migrate memoize-one custom equality function.\nExpected a function or an identifier',
100+
);
101+
});
102+
103+
return source.toSource(options.printOptions);
104+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
export default {
2+
maintainers: ['alexreardon'],
3+
transforms: { '5.0.0': require.resolve('./5.0.0/transform') },
4+
};

0 commit comments

Comments
 (0)