Skip to content

Commit

Permalink
chore: adjust codemod
Browse files Browse the repository at this point in the history
  • Loading branch information
dmaklygin committed Jun 27, 2023
1 parent 8a53efa commit 5507cb7
Show file tree
Hide file tree
Showing 13 changed files with 239 additions and 27 deletions.
7 changes: 7 additions & 0 deletions .changeset/lemon-tigers-pull.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'@toptal/picasso-codemod': minor
---

---

- add codemod for replacing RichTextEditor imports
8 changes: 8 additions & 0 deletions packages/picasso-codemod/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,14 @@ Codemods do not guarantee the code format preservation. Therefore be sure to run

## Included Scripts

### v36.0.0

Replaces all imports of RichTextEditor related components to `@toptal/picasso-rich-text-editor` and updates package.json with a new version of `@toptal/picasso`, `@toptal/picasso-rich-text-editor` and `@toptal/picasso-forms`

```sh
npx @toptal/picasso-codemod v36.0.0 --run-in-band
```

### v52.2.0

Replaces compound `Form` with `FormNonCompound` and adjusts all the compound components to be directly imported from `picasso-forms`.
Expand Down
10 changes: 7 additions & 3 deletions packages/picasso-codemod/bin/cli.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ const require = createRequire(import.meta.url)
const codemodsDirectory = path.join(__dirname, '../', 'src')
const jscodeshift = require.resolve('.bin/jscodeshift')
const paths = {
spa: 'src/**/*.tsx',
spa: 'src/**/*.ts*',
monorepo: {
libs: '*libs/*/src/*',
hosts: '*hosts/*/src/*',
Expand All @@ -28,7 +28,7 @@ const findFilesInMonorepo = () =>
execaSync('find', [
'.',
'-name',
'*.tsx',
'*.ts*',
'(',
...['-path', paths.monorepo.libs],
...['-or', '-path', paths.monorepo.apps],
Expand All @@ -51,7 +51,7 @@ const findCodemodPath = codemod =>
.map(ext => path.join(codemodsDirectory, `${codemod}/index.${ext}`))
.find(fs.existsSync)

const runTransform = async ({ codemod, inputFiles, parserConfig }) => {
const runTransform = async ({ codemod, inputFiles, parserConfig, runInBand }) => {
const codemodPath = findCodemodPath(codemod)
const isMonorepo = await checkIsMonorepo()

Expand All @@ -60,6 +60,7 @@ const runTransform = async ({ codemod, inputFiles, parserConfig }) => {

args = args.concat(['--parser', 'tsx'])
args = args.concat(['--transform', codemodPath])
args = args.concat(['--run-in-band', runInBand])

if (parserConfig) {
args.push(`--parser-config=${parserConfig}`)
Expand Down Expand Up @@ -105,6 +106,7 @@ export const run = () => {
Options
--parser-config Add parser config
--run-in-band Run serially in the current process (default: false)
Examples
$ npx @toptal/picasso-codemod v17.0.0/typography-sizes
Expand All @@ -124,10 +126,12 @@ export const run = () => {
const codemod = cli.input[0]
const inputFiles = cli.input.slice(1)
const parserConfig = cli.flags.parserConfig
const runInBand = cli.flags.runInBand

return runTransform({
codemod,
inputFiles,
parserConfig,
runInBand,
})
}
Original file line number Diff line number Diff line change
@@ -1,2 +1,13 @@
import { RichTextEditor } from '@toptal/picasso-forms'
import type { CustomEmojiGroup } from '@toptal/picasso/RichTextEditor'
import type { CustomEmoji } from '@toptal/picasso/RichTextEditor/types'
import type { RichTextEditorProps as LocalRichTextEditorProps } from '@toptal/picasso'
import {
Container,
RichText as LocalRichText,
Typography,
} from '@toptal/picasso'
import { RichText } from '@toptal/picasso'
import { Icon } from '@toptal/picasso'
import { Something, transformString } from 'some-module'
import { Container, RichText, Typography } from '@toptal/picasso'
import { htmlToHast } from '@toptal/picasso/utils'
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
import { Something, transformString } from 'some-module'
import { RichTextEditor } from '@toptal/picasso-forms'
import type { CustomEmojiGroup } from '@toptal/picasso-rich-text-editor/RichTextEditor'
import type { CustomEmoji } from '@toptal/picasso-rich-text-editor/RichTextEditor/types'
import type { RichTextEditorProps as LocalRichTextEditorProps } from '@toptal/picasso-rich-text-editor'
import { Container, Typography } from '@toptal/picasso'
import { RichText as LocalRichText } from '@toptal/picasso-rich-text-editor'
import { RichText } from '@toptal/picasso-rich-text-editor'
import { Icon } from '@toptal/picasso'
import { Something, transformString } from 'some-module'
import { htmlToHast } from '@toptal/picasso-rich-text-editor/utils'
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@
"license": "ISC",
"main": "./src/index.ts",
"dependencies": {
"@toptal/picasso": "35.0.2",
"@toptal/picasso-forms": "57.0.0"
"@toptal/picasso": "36.0.0",
"@toptal/picasso-forms": "58.0.0",
"@toptal/picasso-rich-text-editor": "1.0.2"
},
"private": true,
"sideEffects": false
Expand Down

This file was deleted.

Empty file.
Empty file.

This file was deleted.

This file was deleted.

5 changes: 5 additions & 0 deletions packages/picasso-codemod/src/v36.0.0/__tests__/test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { defineTest } from 'jscodeshift/src/testUtils'

defineTest(__dirname, 'rich-text-editor-replacement', {}, 'imports', {
parser: 'tsx',
})
193 changes: 189 additions & 4 deletions packages/picasso-codemod/src/v36.0.0/rich-text-editor-replacement.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,194 @@
import type { Transform } from 'jscodeshift'
import path from 'path'
import fs from 'fs'

const transform: Transform = (file, api) => {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const j = api.jscodeshift
// const root = j(file.source)
const specificModules = [
'RichText',
'htmlToHast',
'RichTextEditor',
'RichTextEditorProps',
'ASTType',
'CustomEmojiGroup',
'CustomEmoji',
]
const picassoVersion = '36.0.0'
const picassoFormsVersion = '58.0.0'
const picassoRichTextEditorVersion = '1.0.2'

// Get current execution directory
const execDir = process.cwd()

const findPackageJson = (dirPath: string) => {
// Start with the directory provided
let currentPath = dirPath

while (currentPath !== '/' && currentPath !== execDir) {
// Attempt to read package.json at current path
try {
const packageJsonPath = path.join(currentPath, 'package.json')

if (fs.existsSync(packageJsonPath)) {
return packageJsonPath
}
} catch (err) {
console.error(err)
}

// If package.json doesn't exist, move up one directory
currentPath = path.dirname(currentPath)
}

throw new Error('Could not find package.json')
}

const updatePackageJsonVersions = (
path: string,
{ addRichTextEditorDependency }: { addRichTextEditorDependency: boolean }
) => {
try {
const packageJsonPath = findPackageJson(path)

// If it exists, read and parse it
try {
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'))

if (!packageJson.dependencies) {
return
}

let inDevDependencies = false

// Update Picasso and Picasso forms dependencies
if (packageJson.dependencies['@toptal/picasso']) {
packageJson.dependencies['@toptal/picasso'] = picassoVersion
} else if (
packageJson.devDependencies &&
packageJson.devDependencies['@toptal/picasso']
) {
packageJson.devDependencies['@toptal/picasso'] = picassoVersion
inDevDependencies = true
}

if (packageJson.dependencies['@toptal/picasso-forms']) {
packageJson.dependencies['@toptal/picasso-forms'] = picassoFormsVersion
// if picasso form is defined, it depends on RichTextEditor, we should also add a dependency
packageJson.dependencies['@toptal/picasso-rich-text-editor'] =
picassoRichTextEditorVersion
} else if (
packageJson.devDependencies &&
packageJson.devDependencies['@toptal/picasso-forms']
) {
packageJson.devDependencies['@toptal/picasso-forms'] =
picassoFormsVersion
// if picasso form is defined, it depends on RichTextEditor, we should also add a dependency
packageJson.devDependencies['@toptal/picasso-rich-text-editor'] =
picassoRichTextEditorVersion
inDevDependencies = true
}

// if RTE is used inside package, we should add a dependency
if (addRichTextEditorDependency) {
if (inDevDependencies) {
packageJson.devDependencies['@toptal/picasso-rich-text-editor'] =
picassoRichTextEditorVersion
} else {
packageJson.dependencies['@toptal/picasso-rich-text-editor'] =
picassoRichTextEditorVersion
}
}

fs.writeFileSync(
packageJsonPath,
JSON.stringify(packageJson, null, 2) + '\n'
)
} catch (err) {
console.log(`Could not parse package.json at ${packageJsonPath}: ${err}`)
}
} catch (err) {
console.log('err: ', err)
console.error(`Package json not found for ${path}: ${err}`)
}
}

const transform: Transform = (file, { jscodeshift: j }) => {
const source = j(file.source)
let fileContainsRichTextEditorImport = false
let hasPicassoForms = false

const picassoImports = source.find(j.ImportDeclaration).filter(path => {
const pattern = /@toptal\/picasso($|\/[a-z\d]*)/gi

hasPicassoForms =
hasPicassoForms || path.node.source.value === '@toptal/picasso-forms'

return (
pattern.test(path.node.source.value as string) &&
path.node.source.value !== '@toptal/picasso-forms'
)
})

// Iterate over react imports
picassoImports.forEach(picassoImport => {
const richTextSpecifiers = j(picassoImport)
.find(j.ImportSpecifier)
.filter(path => {
return specificModules.includes(path.node.imported.name)
})

// there is only single specifier, replace the whole import
if (picassoImport.node.specifiers?.length === richTextSpecifiers.length) {
j(picassoImport).replaceWith(
// Build a new import declaration node based on the existing one
j.importDeclaration(
picassoImport.node.specifiers, // copy over the existing import specificers
j.stringLiteral(
(picassoImport.node.source.value as string).replace(
'@toptal/picasso',
'@toptal/picasso-rich-text-editor'
)
),
picassoImport.node.importKind
)
)

fileContainsRichTextEditorImport = true
} else if (richTextSpecifiers.length > 0) {
// insert specifiers for rich text editor
// const importSpecifier = j.importSpecifier(richTextSpecifiers);
// Generate new specifiers for the new import
const newSpecifiers = richTextSpecifiers.nodes().map(node => {
return j.importSpecifier(
j.identifier(node.imported.name),
node.local ? j.identifier(node.local.name) : undefined
)
})
// Create a new import declaration
const newImport = j.importDeclaration(
newSpecifiers,
j.literal('@toptal/picasso-rich-text-editor'),
picassoImport.node.importKind
)

j(picassoImport).insertAfter(newImport)

// there are some specifiers left, remove the ones that are replaced
j(picassoImport)
.find(j.ImportSpecifier)
.filter(path => specificModules.includes(path.node.imported.name))
.remove()

fileContainsRichTextEditorImport = true
}
})

// we must update package.json as well. We have to upgrade @toptal/picasso, @toptal/picasso-forms and add new dependency @toptal/picasso-rich-text-editor
// first of all, we need to search for a package json, it must be in the root of the package
updatePackageJsonVersions(file.path, {
addRichTextEditorDependency:
fileContainsRichTextEditorImport || hasPicassoForms,
})

return source.toSource({ trailingComma: false, quote: 'single' })
}

export default transform

0 comments on commit 5507cb7

Please sign in to comment.