diff --git a/packages/codemod/README.md b/packages/codemod/README.md index 6816c9e..d34257d 100644 --- a/packages/codemod/README.md +++ b/packages/codemod/README.md @@ -1,15 +1,19 @@

- - nextui + + nextui

@heroui/codemod

-The CLI provides a comprehensive suite of tools to migrate your codebase from NextUI to heroui. +
+ +The CLI provides a comprehensive suite of tools to migrate your codebase from NextUI to HeroUI. ## Quick Start > **Note**: The heroui CLI requires [Node.js](https://nodejs.org/en) _18.17.x_ or later +> +> **Note**: If running in monorepo, you need to run the command in the root of your monorepo You can start using @heroui/codemod in one of the following ways: @@ -40,9 +44,10 @@ Options: -v, --version Output the current version -d, --debug Enable debug mode -h, --help Display help for command + -f, --format Format the affected files with Prettier Commands: - migrate [projectPath] Migrate your codebase to use heroui + migrate [projectPath] Migrate your codebase to use heroui ``` ## Codemod Arguments @@ -132,7 +137,7 @@ Example: Migrate your entire codebase from NextUI to heroui. You can choose which codemods to run during the migration process. ```bash -heroui-codemod migrate [projectPath] +heroui-codemod migrate [projectPath] [--format] ``` Example: diff --git a/packages/codemod/package.json b/packages/codemod/package.json index 5a50538..3fb8e15 100644 --- a/packages/codemod/package.json +++ b/packages/codemod/package.json @@ -3,9 +3,9 @@ "private": false, "type": "module", "license": "MIT", - "version": "0.0.2", + "version": "1.1.0", "homepage": "https://github.com/frontio-ai/heroui-cli#readme", - "description": "A CLI tool that modifies your codebase to use the heroui", + "description": "HeroUI Codemod provides transformations to help migrate your codebase from NextUI to HeroUI", "keywords": [ "UI", "CLI", @@ -50,16 +50,18 @@ "lint:fix": "eslint . --max-warnings=0 --fix", "check:prettier": "prettier --check .", "check:types": "tsc --noEmit", - "changelog": "npx conventional-changelog -p angular -i CHANGELOG.md -s --commit-path packages/codemod", - "release": "bumpp --execute='pnpm run changelog' --all --tag='@heroui/codemodv%s'", + "changelog": "npx conventional-changelog -p angular -i CHANGELOG.md -s --commit-path .", + "release": "bumpp --execute='pnpm run changelog' --all --tag '@heroui/codemodv%s'", "prepublishOnly": "pnpm run build" }, "dependencies": { "@clack/prompts": "0.7.0", "async-retry": "1.3.3", "chalk": "5.3.0", + "@winches/prompts": "0.0.6", "cli-progress": "3.12.0", "commander": "11.0.0", + "find-up": "7.0.0", "compare-versions": "6.1.1", "fast-glob": "3.3.2", "gradient-string": "2.0.2", diff --git a/packages/codemod/src/actions/migrate-action.ts b/packages/codemod/src/actions/migrate-action.ts index ae70fdd..7b75d7b 100644 --- a/packages/codemod/src/actions/migrate-action.ts +++ b/packages/codemod/src/actions/migrate-action.ts @@ -1,21 +1,25 @@ import type {Codemods} from '../types'; import * as p from '@clack/prompts'; +import {exec} from '@helpers/exec'; import {Logger} from '@helpers/logger'; import chalk from 'chalk'; import {confirmClack} from 'src/prompts/clack'; -import {NEXTUI_PREFIX} from '../constants/prefix'; +import {EXTRA_FILES} from '../constants/prefix'; +import {lintAffectedFiles} from '../helpers/actions/lint-affected-files'; import {migrateCssVariables} from '../helpers/actions/migrate/migrate-css-variables'; import {migrateImportPackageWithPaths} from '../helpers/actions/migrate/migrate-import'; import {migrateJson} from '../helpers/actions/migrate/migrate-json'; +import {migrateLeftFiles} from '../helpers/actions/migrate/migrate-left-files'; import {migrateNextuiProvider} from '../helpers/actions/migrate/migrate-nextui-provider'; import {migrateNpmrc} from '../helpers/actions/migrate/migrate-npmrc'; import {migrateTailwindcss} from '../helpers/actions/migrate/migrate-tailwindcss'; import {findFiles} from '../helpers/find-files'; -import {getStore, storeParsedContent, storePathsRawContent} from '../helpers/store'; +import {getOptionsValue} from '../helpers/options'; +import {affectedFiles, storeParsedContent, storePathsRawContent} from '../helpers/store'; import {transformPaths} from '../helpers/transform'; -import {getCanRunCodemod} from '../helpers/utils'; +import {filterNextuiFiles, getCanRunCodemod, getInstallCommand} from '../helpers/utils'; process.on('SIGINT', () => { Logger.newLine(); @@ -30,7 +34,10 @@ interface MigrateActionOptions { export async function migrateAction(projectPaths?: string[], options = {} as MigrateActionOptions) { const {codemod} = options; const transformedPaths = transformPaths(projectPaths); - const files = await findFiles(transformedPaths, {ext: '{js,jsx,ts,tsx,json}'}); + const baseFiles = await findFiles(transformedPaths, {ext: '{js,jsx,ts,tsx,json,mjs,cjs}'}); + const dotFiles = await findFiles(transformedPaths, {dot: true}); + const extraFiles = dotFiles.filter((file) => EXTRA_FILES.some((extra) => file.includes(extra))); + const files = [...baseFiles, ...extraFiles]; // Store the raw content of the files storePathsRawContent(files); @@ -38,12 +45,10 @@ export async function migrateAction(projectPaths?: string[], options = {} as Mig // All package.json const packagesJson = files.filter((file) => file.includes('package.json')); // All included nextui - const nextuiFiles = files.filter((file) => - new RegExp(NEXTUI_PREFIX, 'g').test(getStore(file, 'rawContent')) - ); + const nextuiFiles = filterNextuiFiles(files); let step = 1; - p.intro(chalk.inverse(' Starting to migrate nextui to heroui ')); + p.intro(chalk.inverse('Starting to migrate NextUI to HeroUI')); /** ======================== 1. Migrate package.json ======================== */ const runMigratePackageJson = getCanRunCodemod(codemod, 'package-json-package-name'); @@ -64,7 +69,7 @@ export async function migrateAction(projectPaths?: string[], options = {} as Mig const runMigrateImportNextui = getCanRunCodemod(codemod, 'import-heroui'); if (runMigrateImportNextui) { - p.log.step(`${step}. Migrating import "nextui" to "heorui"`); + p.log.step(`${step}. Migrating import "nextui" to "heroui"`); const selectMigrateNextui = await confirmClack({ message: 'Do you want to migrate import nextui to heroui?' }); @@ -129,9 +134,7 @@ export async function migrateAction(projectPaths?: string[], options = {} as Mig const runMigrateNpmrc = getCanRunCodemod(codemod, 'npmrc'); if (runMigrateNpmrc) { - const npmrcFiles = (await findFiles(transformedPaths, {dot: true})).filter((path) => - path.includes('.npmrc') - ); + const npmrcFiles = dotFiles.filter((path) => path.includes('.npmrc')); p.log.step(`${step}. Migrating "npmrc" (Pnpm only)`); const selectMigrateNpmrc = await confirmClack({ @@ -144,5 +147,76 @@ export async function migrateAction(projectPaths?: string[], options = {} as Mig step++; } + /** ======================== 7. Whether need to change left files with @nextui-org ======================== */ + const remainingNextuiFiles = filterNextuiFiles([...affectedFiles]); + const remainingFiles = [ + ...nextuiFiles.filter((file) => !affectedFiles.has(file)), + ...remainingNextuiFiles + ]; + const runCheckLeftFiles = remainingFiles.length > 0; + + // If user not using individual codemod, we need to ask user to replace left files + if (runCheckLeftFiles && !codemod) { + p.log.step(`${step}. Remaining files with "@nextui-org" (${remainingFiles.length})`); + p.log.info(remainingFiles.join('\n')); + const selectMigrateLeftFiles = await confirmClack({ + message: 'Do you want to replace all remaining instances of "@nextui-org" with "@heroui"?' + }); + + if (selectMigrateLeftFiles) { + migrateLeftFiles(remainingFiles); + } + step++; + } + + const format = getOptionsValue('format'); + /** ======================== 8. Formatting affected files (Optional) ======================== */ + const runFormatAffectedFiles = affectedFiles.size > 0; + + // If user using format option, we don't need to use eslint + if (runFormatAffectedFiles && !format) { + p.log.step(`${step}. Formatting affected files (Optional)`); + const selectMigrateNpmrc = await confirmClack({ + message: `Do you want to format affected files? (${affectedFiles.size})` + }); + + if (selectMigrateNpmrc) { + await lintAffectedFiles(); + } + step++; + } + + // Directly linting affected files don't need to ask user + if (format) { + await lintAffectedFiles(); + } + + /** ======================== 9. Reinstall the dependencies ======================== */ + // if package.json is affected, we need to ask user to reinstall the dependencies + const runReinstallDependencies = [...affectedFiles.keys()].some((file) => + file.includes('package.json') + ); + + if (runReinstallDependencies) { + p.log.step(`${step}. Reinstalling the dependencies`); + const selectReinstallDependencies = await confirmClack({ + message: 'Do you want to reinstall the dependencies?' + }); + + if (selectReinstallDependencies) { + const {cmd} = await getInstallCommand(); + + try { + await exec(cmd); + } catch { + p.log.error(`Failed to reinstall dependencies. Please run "${cmd}" manually.`); + } + } else { + // If user doesn't want to reinstall the dependencies automatically, tell them to run it manually + p.note(`Please reinstall the dependencies (e.g., "pnpm install")`, 'Next steps'); + } + step++; + } + p.outro(chalk.green('✅ Migration completed!')); } diff --git a/packages/codemod/src/constants/prefix.ts b/packages/codemod/src/constants/prefix.ts index d2ce36c..46475e0 100644 --- a/packages/codemod/src/constants/prefix.ts +++ b/packages/codemod/src/constants/prefix.ts @@ -9,3 +9,5 @@ export const HEROUI_PLUGIN = 'heroui'; export const NEXTUI_CSS_VARIABLES_PREFIX = '--nextui-'; export const HEROUI_CSS_VARIABLES_PREFIX = '--heroui-'; + +export const EXTRA_FILES = ['.storybook']; diff --git a/packages/codemod/src/helpers/actions/lint-affected-files.ts b/packages/codemod/src/helpers/actions/lint-affected-files.ts new file mode 100644 index 0000000..e331d78 --- /dev/null +++ b/packages/codemod/src/helpers/actions/lint-affected-files.ts @@ -0,0 +1,10 @@ +import {tryLintFile} from '../lint'; +import {affectedFiles} from '../store'; + +export async function lintAffectedFiles() { + try { + await tryLintFile(Array.from(affectedFiles)); + } catch (error) { + return; + } +} diff --git a/packages/codemod/src/helpers/actions/migrate/migrate-common.ts b/packages/codemod/src/helpers/actions/migrate/migrate-common.ts index 9a69548..58d50b7 100644 --- a/packages/codemod/src/helpers/actions/migrate/migrate-common.ts +++ b/packages/codemod/src/helpers/actions/migrate/migrate-common.ts @@ -100,6 +100,20 @@ export function migrateJSXElementName( return dirtyFlag; } +export function migrateByRegex(rawContent: string, match: string, replace: string) { + const regex = new RegExp(match, 'g'); + const dirtyFlag = regex.test(rawContent); + + if (dirtyFlag) { + rawContent = rawContent.replace(regex, replace); + } + + return { + dirtyFlag, + rawContent + }; +} + /** * Migrate the name of the CallExpression * @example diff --git a/packages/codemod/src/helpers/actions/migrate/migrate-css-variables.ts b/packages/codemod/src/helpers/actions/migrate/migrate-css-variables.ts index 3446e87..1bd92a8 100644 --- a/packages/codemod/src/helpers/actions/migrate/migrate-css-variables.ts +++ b/packages/codemod/src/helpers/actions/migrate/migrate-css-variables.ts @@ -1,5 +1,5 @@ import {HEROUI_CSS_VARIABLES_PREFIX, NEXTUI_CSS_VARIABLES_PREFIX} from '../../../constants/prefix'; -import {getStore, writeFileAndUpdateStore} from '../../store'; +import {getStore, updateAffectedFiles, writeFileAndUpdateStore} from '../../store'; export function migrateCssVariables(files: string[]) { for (const file of files) { @@ -13,6 +13,7 @@ export function migrateCssVariables(files: string[]) { ); writeFileAndUpdateStore(file, 'rawContent', content); + updateAffectedFiles(file); } } } diff --git a/packages/codemod/src/helpers/actions/migrate/migrate-import.ts b/packages/codemod/src/helpers/actions/migrate/migrate-import.ts index 1a30010..bfedcbb 100644 --- a/packages/codemod/src/helpers/actions/migrate/migrate-import.ts +++ b/packages/codemod/src/helpers/actions/migrate/migrate-import.ts @@ -1,7 +1,12 @@ import jscodeshift from 'jscodeshift'; import {HEROUI_PREFIX, NEXTUI_PREFIX} from '../../../constants/prefix'; -import {type StoreObject, getStore, writeFileAndUpdateStore} from '../../store'; +import { + type StoreObject, + getStore, + updateAffectedFiles, + writeFileAndUpdateStore +} from '../../store'; /** * Migrate the import package will directly write the file @@ -24,6 +29,7 @@ export function migrateImportPackageWithPaths(paths: string[]) { if (dirtyFlag) { // Write the modified content back to the file writeFileAndUpdateStore(path, 'parsedContent', parsedContent); + updateAffectedFiles(path); } // eslint-disable-next-line no-empty } catch {} diff --git a/packages/codemod/src/helpers/actions/migrate/migrate-json.ts b/packages/codemod/src/helpers/actions/migrate/migrate-json.ts index 9eb9469..6b2e616 100644 --- a/packages/codemod/src/helpers/actions/migrate/migrate-json.ts +++ b/packages/codemod/src/helpers/actions/migrate/migrate-json.ts @@ -5,9 +5,10 @@ import {Logger} from '@helpers/logger'; import {HEROUI_PREFIX, NEXTUI_PREFIX} from '../../../constants/prefix'; import {fetchPackageLatestVersion} from '../../https'; import {safeParseJson} from '../../parse'; -import {getStore, writeFileAndUpdateStore} from '../../store'; +import {getStore, updateAffectedFiles, writeFileAndUpdateStore} from '../../store'; const DEFAULT_INDENT = 2; +const LATEST_VERSION = 'latest'; export function detectIndent(content: string): number { const match = content.match(/^(\s+)/m); @@ -33,14 +34,22 @@ export async function migrateJson(files: string[]) { try { await Promise.all([ ...filterHeroUiPkgs(Object.keys(json.dependencies)).map(async (key) => { - const version = await fetchPackageLatestVersion(key); + try { + const version = await fetchPackageLatestVersion(key); - json.dependencies[key] = version; + json.dependencies[key] = version; + } catch (error) { + json.dependencies[key] = LATEST_VERSION; + } }), ...filterHeroUiPkgs(Object.keys(json.devDependencies)).map(async (key) => { - const version = await fetchPackageLatestVersion(key); + try { + const version = await fetchPackageLatestVersion(key); - json.devDependencies[key] = version; + json.devDependencies[key] = version; + } catch (error) { + json.devDependencies[key] = LATEST_VERSION; + } }) ]); } catch (error) { @@ -51,6 +60,7 @@ export async function migrateJson(files: string[]) { const indent = detectIndent(content); writeFileAndUpdateStore(file, 'rawContent', JSON.stringify(json, null, indent)); + updateAffectedFiles(file); } }) ); diff --git a/packages/codemod/src/helpers/actions/migrate/migrate-left-files.ts b/packages/codemod/src/helpers/actions/migrate/migrate-left-files.ts new file mode 100644 index 0000000..a00f89c --- /dev/null +++ b/packages/codemod/src/helpers/actions/migrate/migrate-left-files.ts @@ -0,0 +1,19 @@ +import { + HEROUI_PLUGIN, + HEROUI_PREFIX, + NEXTUI_PLUGIN, + NEXTUI_PREFIX +} from '../../../constants/prefix'; +import {getStore, updateAffectedFiles, writeFileAndUpdateStore} from '../../store'; + +export function migrateLeftFiles(files: string[]) { + for (const file of files) { + const rawContent = getStore(file, 'rawContent'); + const replaceContent = rawContent + .replaceAll(NEXTUI_PREFIX, HEROUI_PREFIX) + .replaceAll(NEXTUI_PLUGIN, HEROUI_PLUGIN); + + writeFileAndUpdateStore(file, 'rawContent', replaceContent); + updateAffectedFiles(file); + } +} diff --git a/packages/codemod/src/helpers/actions/migrate/migrate-nextui-provider.ts b/packages/codemod/src/helpers/actions/migrate/migrate-nextui-provider.ts index c99be60..862a703 100644 --- a/packages/codemod/src/helpers/actions/migrate/migrate-nextui-provider.ts +++ b/packages/codemod/src/helpers/actions/migrate/migrate-nextui-provider.ts @@ -1,7 +1,7 @@ import {HEROUI_PROVIDER, NEXTUI_PROVIDER} from '../../../constants/prefix'; -import {getStore, writeFileAndUpdateStore} from '../../store'; +import {getStore, updateAffectedFiles, writeFileAndUpdateStore} from '../../store'; -import {migrateImportName, migrateJSXElementName} from './migrate-common'; +import {migrateByRegex} from './migrate-common'; /** * Migrate the NextUIProvider to HeroUIProvider will directly write the file @@ -12,22 +12,21 @@ import {migrateImportName, migrateJSXElementName} from './migrate-common'; export function migrateNextuiProvider(paths: string[]) { for (const path of paths) { try { - const parsedContent = getStore(path, 'parsedContent'); + let rawContent = getStore(path, 'rawContent'); let dirtyFlag = false; - if (!parsedContent) { + if (!rawContent) { continue; } // Replace JSX element NextUIProvider with HeroUIProvider - dirtyFlag = migrateJSXElementName(parsedContent, NEXTUI_PROVIDER, HEROUI_PROVIDER); - // Replace NextUIProvider with HeroUIProvider in import statements - if (dirtyFlag) { - migrateImportName(parsedContent, NEXTUI_PROVIDER, HEROUI_PROVIDER); + ({dirtyFlag, rawContent} = migrateByRegex(rawContent, NEXTUI_PROVIDER, HEROUI_PROVIDER)); + if (dirtyFlag) { // Write the modified content back to the file - writeFileAndUpdateStore(path, 'parsedContent', parsedContent); + writeFileAndUpdateStore(path, 'rawContent', rawContent); + updateAffectedFiles(path); } // eslint-disable-next-line no-empty } catch {} diff --git a/packages/codemod/src/helpers/actions/migrate/migrate-tailwindcss.ts b/packages/codemod/src/helpers/actions/migrate/migrate-tailwindcss.ts index 76c596e..ca6a0b8 100644 --- a/packages/codemod/src/helpers/actions/migrate/migrate-tailwindcss.ts +++ b/packages/codemod/src/helpers/actions/migrate/migrate-tailwindcss.ts @@ -8,7 +8,7 @@ import { NEXTUI_PLUGIN, NEXTUI_PREFIX } from '../../../constants/prefix'; -import {getStore, writeFileAndUpdateStore} from '../../store'; +import {getStore, updateAffectedFiles, writeFileAndUpdateStore} from '../../store'; import {migrateCallExpressionName, migrateImportName} from './migrate-common'; import {migrateImportPackage} from './migrate-import'; @@ -56,6 +56,7 @@ export function migrateTailwindcss(paths: string[]) { if (dirtyFlag) { writeFileAndUpdateStore(path, 'parsedContent', parsedContent); + updateAffectedFiles(path); } } } diff --git a/packages/codemod/src/helpers/https.ts b/packages/codemod/src/helpers/https.ts index f77bbbd..6947b00 100644 --- a/packages/codemod/src/helpers/https.ts +++ b/packages/codemod/src/helpers/https.ts @@ -1,3 +1,4 @@ +import retry from 'async-retry'; import chalk from 'chalk'; import ora from 'ora'; @@ -25,30 +26,28 @@ export async function fetchPackageLatestVersion(packageName: string): Promise controller.abort(), 15000); // 15 seconds timeout + return await retry( + async () => { + const response = await fetch(`https://registry.npmjs.org/${packageName}/latest`, { + headers: { + Accept: 'application/json' + } + }); - const response = await fetch(`https://registry.npmjs.org/${packageName}/latest`, { - headers: { - Accept: 'application/json' - }, - signal: controller.signal - }); - - clearTimeout(timeoutId); - - if (!response.ok) { - throw new Error(`Request failed with status ${response.status}`); - } + if (!response.ok) { + throw new Error(`Request failed with status ${response.status}`); + } - const data = await response.json(); + const data = await response.json(); - return (data as {version: string}).version; + return (data as {version: string}).version; + }, + { + retries: 2 + } + ); } catch (error) { if (error instanceof Error) { - if (error.name === 'AbortError') { - throw new Error('Request timeout'); - } if (error.message.includes('fetch failed')) { throw new Error('Connection failed. Please check your network connection.'); } diff --git a/packages/codemod/src/helpers/lint.ts b/packages/codemod/src/helpers/lint.ts new file mode 100644 index 0000000..a015d58 --- /dev/null +++ b/packages/codemod/src/helpers/lint.ts @@ -0,0 +1,58 @@ +import {getOptionsValue} from './options'; +import {getStore, writeFileAndUpdateStore} from './store'; + +async function tryImportPackage(packageName: string) { + try { + return await import(packageName); + } catch { + return null; + } +} + +export async function lintWithESLint(filePaths: string[]) { + const eslintPkg = await tryImportPackage('eslint'); + + if (eslintPkg) { + const ESLint = eslintPkg.ESLint; + const eslint = new ESLint({ + fix: true + }); + const result = await eslint.lintFiles(filePaths); + + await ESLint.outputFixes(result); + } +} + +export async function lintWithPrettier(filePaths: string[]) { + const prettier = await tryImportPackage('prettier'); + const options = await prettier.resolveConfig(process.cwd()); + + if (prettier) { + await Promise.all( + filePaths.map(async (filePath) => { + const rawContent = getStore(filePath, 'rawContent'); + const formattedContent = await prettier.format(rawContent, { + options, + parser: 'typescript' + }); + + writeFileAndUpdateStore(filePath, 'rawContent', formattedContent); + }) + ); + } +} + +/** + * Try linting a file with ESLint or Prettier + */ +export async function tryLintFile(filePaths: string[]) { + try { + if (getOptionsValue('format')) { + await lintWithPrettier(filePaths); + } else { + await lintWithESLint(filePaths); + } + } catch { + return; + } +} diff --git a/packages/codemod/src/helpers/options.ts b/packages/codemod/src/helpers/options.ts new file mode 100644 index 0000000..01ce731 --- /dev/null +++ b/packages/codemod/src/helpers/options.ts @@ -0,0 +1,17 @@ +export const CODEMOD_OPTIONS = { + format: false +}; + +export function initOptions(options: {format: boolean}) { + const {format} = options; + + setOptionsValue('format', format); +} + +export function setOptionsValue(key: keyof typeof CODEMOD_OPTIONS, value: boolean) { + CODEMOD_OPTIONS[key] = value; +} + +export function getOptionsValue(key: T) { + return CODEMOD_OPTIONS[key]; +} diff --git a/packages/codemod/src/helpers/store.ts b/packages/codemod/src/helpers/store.ts index e1685c2..9d9e50f 100644 --- a/packages/codemod/src/helpers/store.ts +++ b/packages/codemod/src/helpers/store.ts @@ -100,3 +100,9 @@ export function writeFileAndUpdateStore( updateStore(path, key, value); key === 'parsedContent' && updateStore(path, 'rawContent', data.rawContent); } + +export const affectedFiles = new Set(); + +export function updateAffectedFiles(path: string) { + affectedFiles.add(path); +} diff --git a/packages/codemod/src/helpers/utils.ts b/packages/codemod/src/helpers/utils.ts index 4d400cd..8e93cb6 100644 --- a/packages/codemod/src/helpers/utils.ts +++ b/packages/codemod/src/helpers/utils.ts @@ -1,5 +1,24 @@ import type {Codemods} from '../types'; +import {detect} from '@helpers/detect'; + +import {NEXTUI_PREFIX} from '../constants/prefix'; + +import {getStore} from './store'; + export function getCanRunCodemod(codemod: Codemods, targetName: Codemods) { return codemod === undefined || codemod === targetName; } + +export function filterNextuiFiles(files: string[]) { + return files.filter((file) => new RegExp(NEXTUI_PREFIX, 'g').test(getStore(file, 'rawContent'))); +} + +export async function getInstallCommand() { + const packageManager = await detect(); + + return { + cmd: `${packageManager} install`, + packageManager + }; +} diff --git a/packages/codemod/src/index.ts b/packages/codemod/src/index.ts index c09b831..fffdded 100644 --- a/packages/codemod/src/index.ts +++ b/packages/codemod/src/index.ts @@ -9,6 +9,7 @@ import pkg from '../package.json'; import {codemodAction} from './actions/codemod-action'; import {migrateAction} from './actions/migrate-action'; import {DEBUG} from './helpers/debug'; +import {initOptions} from './helpers/options'; import {codemods} from './types'; const heroui = new Command(); @@ -22,6 +23,7 @@ heroui .argument('[codemod]', `Specify which codemod to run\nCodemods: ${codemods.join(', ')}`) .allowUnknownOption() .option('-d, --debug', 'Enable debug mode') + .option('-f, --format', 'Format the affected files with Prettier') .action(codemodAction); heroui @@ -33,6 +35,9 @@ heroui heroui.hook('preAction', async (command) => { const options = (command as SAFE_ANY).rawArgs.slice(2); const debug = options.includes('--debug') || options.includes('-d'); + const format = options.includes('--format') || options.includes('-f'); + + initOptions({format}); DEBUG.enabled = debug; }); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 36fffc5..04d07f1 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -126,6 +126,9 @@ importers: '@clack/prompts': specifier: 0.7.0 version: 0.7.0 + '@winches/prompts': + specifier: 0.0.6 + version: 0.0.6 async-retry: specifier: 1.3.3 version: 1.3.3 @@ -144,6 +147,9 @@ importers: fast-glob: specifier: 3.3.2 version: 3.3.2 + find-up: + specifier: 7.0.0 + version: 7.0.0 gradient-string: specifier: 2.0.2 version: 2.0.2