Add per-file codemod for updating useLingui to v5 (#10505)
This commit is contained in:
@@ -0,0 +1,236 @@
|
|||||||
|
/**
|
||||||
|
* Codemod to update Lingui's useLingui() hook to use v5.
|
||||||
|
*
|
||||||
|
* Before:
|
||||||
|
* import {msg} from '@lingui/core/macro'
|
||||||
|
* import {useLingui} from '@lingui/react'
|
||||||
|
* const {_} = useLingui()
|
||||||
|
* _(msg`Lorem ipsum`)
|
||||||
|
*
|
||||||
|
* After:
|
||||||
|
* import {useLingui} from '@lingui/react/macro'
|
||||||
|
* const {t: l} = useLingui()
|
||||||
|
* l`Lorem ipsum`
|
||||||
|
*
|
||||||
|
* Usage: jscodeshift -t .jscodeshift/file/lingui-v5.js <file-path>
|
||||||
|
* Example: jscodeshift -t .jscodeshift/file/lingui-v5.js modules/bottom-sheet/src/lib/Portal.tsx
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* eslint-disable */
|
||||||
|
|
||||||
|
export const parser = 'tsx'
|
||||||
|
|
||||||
|
export default function transformer(file, api) {
|
||||||
|
const j = api.jscodeshift
|
||||||
|
|
||||||
|
const root = j(file.source)
|
||||||
|
|
||||||
|
// Remove msg imports, including aliased imports
|
||||||
|
root
|
||||||
|
.find(j.ImportDeclaration)
|
||||||
|
.filter(path => path.node.source.value === '@lingui/core/macro')
|
||||||
|
.forEach(path => {
|
||||||
|
const newSpecifiers = path.node.specifiers.filter(specifier => {
|
||||||
|
if (j.ImportSpecifier.check(specifier)) {
|
||||||
|
const importedName = specifier.imported
|
||||||
|
? specifier.imported.name
|
||||||
|
: specifier.local.name
|
||||||
|
const localName = specifier.local
|
||||||
|
? specifier.local.name
|
||||||
|
: importedName
|
||||||
|
|
||||||
|
return !(
|
||||||
|
importedName === 'msg' &&
|
||||||
|
(localName === 'msg' || localName === 'msgLingui')
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
|
||||||
|
if (newSpecifiers.length > 0) {
|
||||||
|
path.node.specifiers = newSpecifiers
|
||||||
|
} else {
|
||||||
|
j(path).remove()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// Update React imports
|
||||||
|
root
|
||||||
|
.find(j.ImportDeclaration)
|
||||||
|
.filter(path => path.node.source.value === '@lingui/react')
|
||||||
|
.forEach(path => {
|
||||||
|
path.node.source = j.stringLiteral('@lingui/react/macro')
|
||||||
|
})
|
||||||
|
|
||||||
|
// Replace {_} with {t: l}
|
||||||
|
root
|
||||||
|
.find(j.VariableDeclarator, {
|
||||||
|
init: {
|
||||||
|
type: 'CallExpression',
|
||||||
|
callee: {name: 'useLingui'},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.replaceWith(nodePath => {
|
||||||
|
const {node} = nodePath
|
||||||
|
for (let i = 0; i < node.id.properties.length; i++) {
|
||||||
|
if (node.id.properties[i].key.name === '_') {
|
||||||
|
node.id.properties[i].key = j.identifier('t')
|
||||||
|
node.id.properties[i].shorthand = false
|
||||||
|
node.id.properties[i].value = j.identifier('l')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return node
|
||||||
|
})
|
||||||
|
|
||||||
|
// Replace {_: _l} with {t: l}
|
||||||
|
root
|
||||||
|
.find(j.VariableDeclarator, {
|
||||||
|
init: {callee: {name: 'useLingui'}},
|
||||||
|
id: {
|
||||||
|
properties: properties =>
|
||||||
|
properties.some(p => p.key.name === '_' && p.value.name === '_l'),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.forEach(path => {
|
||||||
|
const prop = path.node.id.properties.find(
|
||||||
|
p => p.key.name === '_' && p.value.name === '_l',
|
||||||
|
)
|
||||||
|
if (prop) {
|
||||||
|
prop.key.name = 't'
|
||||||
|
prop.value.name = 'l'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// Replace _(msg``) with l``
|
||||||
|
root
|
||||||
|
.find(j.CallExpression, {
|
||||||
|
callee: {name: '_'},
|
||||||
|
arguments: [
|
||||||
|
{
|
||||||
|
type: 'TaggedTemplateExpression',
|
||||||
|
tag: {name: 'msg'},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
.replaceWith(path => {
|
||||||
|
const quasis = path.node.arguments[0].quasi
|
||||||
|
|
||||||
|
return j.taggedTemplateExpression(j.identifier('l'), quasis)
|
||||||
|
})
|
||||||
|
|
||||||
|
// Replace _l(msgLingui``) with l``
|
||||||
|
root
|
||||||
|
.find(j.CallExpression, {
|
||||||
|
callee: {name: '_l'},
|
||||||
|
arguments: [
|
||||||
|
{
|
||||||
|
type: 'TaggedTemplateExpression',
|
||||||
|
tag: {name: 'msgLingui'},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
.replaceWith(path => {
|
||||||
|
const quasis = path.node.arguments[0].quasi
|
||||||
|
|
||||||
|
return j.taggedTemplateExpression(j.identifier('l'), quasis)
|
||||||
|
})
|
||||||
|
|
||||||
|
// Replace _(msg()) with l()
|
||||||
|
root
|
||||||
|
.find(j.CallExpression, {
|
||||||
|
callee: {name: '_'},
|
||||||
|
arguments: [
|
||||||
|
{
|
||||||
|
type: 'CallExpression',
|
||||||
|
callee: {name: 'msg'},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
.replaceWith(path => {
|
||||||
|
return j.callExpression(j.identifier('l'), [
|
||||||
|
path.node.arguments[0].arguments[0],
|
||||||
|
])
|
||||||
|
})
|
||||||
|
|
||||||
|
// Replace _l(msgLingui()) with l()
|
||||||
|
root
|
||||||
|
.find(j.CallExpression, {
|
||||||
|
callee: {name: '_l'},
|
||||||
|
arguments: [
|
||||||
|
{
|
||||||
|
type: 'CallExpression',
|
||||||
|
callee: {name: 'msgLingui'},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
.replaceWith(path => {
|
||||||
|
return j.callExpression(j.identifier('l'), [
|
||||||
|
path.node.arguments[0].arguments[0],
|
||||||
|
])
|
||||||
|
})
|
||||||
|
|
||||||
|
// Replace _(plural()) with plural()
|
||||||
|
root
|
||||||
|
.find(j.CallExpression, {
|
||||||
|
callee: {
|
||||||
|
type: 'Identifier',
|
||||||
|
name: '_',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.forEach(path => {
|
||||||
|
const callExpression = path.node
|
||||||
|
if (
|
||||||
|
callExpression.arguments.length === 1 &&
|
||||||
|
callExpression.arguments[0].type === 'CallExpression'
|
||||||
|
) {
|
||||||
|
const innerCall = callExpression.arguments[0]
|
||||||
|
|
||||||
|
if (
|
||||||
|
innerCall.callee.type === 'Identifier' &&
|
||||||
|
innerCall.callee.name === 'plural'
|
||||||
|
) {
|
||||||
|
j(path).replaceWith(innerCall)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// Update hook dependency arrays
|
||||||
|
root
|
||||||
|
.find(j.CallExpression, {
|
||||||
|
callee: callee => {
|
||||||
|
if (
|
||||||
|
callee.type === 'Identifier' ||
|
||||||
|
callee.type === 'MemberExpression'
|
||||||
|
) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
['useCallback', 'useEffect', 'useMemo'].includes(callee.name) &&
|
||||||
|
callee.type === 'MemberExpression' &&
|
||||||
|
callee.object.type === 'Identifier' &&
|
||||||
|
callee.object.name === 'React' &&
|
||||||
|
callee.property.type === 'Identifier' &&
|
||||||
|
callee.property.name === 'useCallback'
|
||||||
|
) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.forEach(path => {
|
||||||
|
const dependencyArray = path.node.arguments[1]
|
||||||
|
if (dependencyArray && dependencyArray.type === 'ArrayExpression') {
|
||||||
|
dependencyArray.elements = dependencyArray.elements.map(element => {
|
||||||
|
if (
|
||||||
|
element.type === 'Identifier' &&
|
||||||
|
(element.name === '_' || element.name === '_l')
|
||||||
|
) {
|
||||||
|
return j.identifier('l')
|
||||||
|
}
|
||||||
|
return element
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return root.toSource()
|
||||||
|
}
|
||||||
@@ -9,8 +9,8 @@
|
|||||||
* import { useEffect } from 'react'
|
* import { useEffect } from 'react'
|
||||||
* useEffect(() => {}, [])
|
* useEffect(() => {}, [])
|
||||||
*
|
*
|
||||||
* Usage: jscodeshift -t .jscodeshift/react-import.js <file-path>
|
* Usage: jscodeshift -t .jscodeshift/repo/react-import.js <file-path>
|
||||||
* Example: jscodeshift -t .jscodeshift/react-import.js src/App.native.tsx
|
* Example: jscodeshift -t .jscodeshift/repo/react-import.js src/App.native.tsx
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* eslint-disable */
|
/* eslint-disable */
|
||||||
@@ -9,8 +9,8 @@
|
|||||||
* import * as Toast from '#/components/Toast'
|
* import * as Toast from '#/components/Toast'
|
||||||
* Toast.show(message, {type: 'error'})
|
* Toast.show(message, {type: 'error'})
|
||||||
*
|
*
|
||||||
* Usage: jscodeshift -t .jscodeshift/toast-v2.js <file-path>
|
* Usage: jscodeshift -t .jscodeshift/repo/toast-v2.js <file-path>
|
||||||
* Example: jscodeshift -t .jscodeshift/toast-v2.js src/App.native.tsx
|
* Example: jscodeshift -t .jscodeshift/repo/toast-v2.js src/App.native.tsx
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* eslint-disable */
|
/* eslint-disable */
|
||||||
Reference in New Issue
Block a user