diff --git a/packages/codemod/src/actions/migrate-action.ts b/packages/codemod/src/actions/migrate-action.ts index 8192441..ae70fdd 100644 --- a/packages/codemod/src/actions/migrate-action.ts +++ b/packages/codemod/src/actions/migrate-action.ts @@ -44,7 +44,6 @@ export async function migrateAction(projectPaths?: string[], options = {} as Mig let step = 1; p.intro(chalk.inverse(' Starting to migrate nextui to heroui ')); - const spinner = p.spinner(); /** ======================== 1. Migrate package.json ======================== */ const runMigratePackageJson = getCanRunCodemod(codemod, 'package-json-package-name'); @@ -56,9 +55,7 @@ export async function migrateAction(projectPaths?: string[], options = {} as Mig }); if (selectMigrate) { - spinner.start('Migrating package.json...'); await migrateJson(packagesJson); - spinner.stop('Migrated package.json'); } step++; } @@ -76,9 +73,7 @@ export async function migrateAction(projectPaths?: string[], options = {} as Mig // Store all the parsed content of the nextuiFiles storeParsedContent(nextuiFiles); - spinner.start('Migrating import nextui to heroui...'); migrateImportPackageWithPaths(nextuiFiles); - spinner.stop('Migrated import nextui to heroui'); } step++; } @@ -93,9 +88,7 @@ export async function migrateAction(projectPaths?: string[], options = {} as Mig }); if (selectMigrateNextuiProvider) { - spinner.start('Migrating NextUIProvider to HeroUIProvider...'); migrateNextuiProvider(nextuiFiles); - spinner.stop('Migrated NextUIProvider to HeroUIProvider'); } step++; } @@ -112,9 +105,7 @@ export async function migrateAction(projectPaths?: string[], options = {} as Mig if (selectMigrateTailwindcss) { const tailwindcssFiles = files.filter((file) => /tailwind\.config\.[jt]s/.test(file)); - spinner.start('Migrating tailwindcss...'); migrateTailwindcss(tailwindcssFiles); - spinner.stop('Migrated tailwindcss'); } step++; } @@ -129,9 +120,7 @@ export async function migrateAction(projectPaths?: string[], options = {} as Mig }); if (selectMigrateCssVariables) { - spinner.start('Migrating css variables...'); migrateCssVariables(files); - spinner.stop('Migrated css variables'); } step++; } @@ -150,9 +139,7 @@ export async function migrateAction(projectPaths?: string[], options = {} as Mig }); if (selectMigrateNpmrc) { - spinner.start('Migrating npmrc...'); migrateNpmrc(npmrcFiles); - spinner.stop('Migrated npmrc'); } step++; } diff --git a/packages/codemod/src/helpers/actions/migrate/migrate-json.ts b/packages/codemod/src/helpers/actions/migrate/migrate-json.ts index 7b3c7ef..9eb9469 100644 --- a/packages/codemod/src/helpers/actions/migrate/migrate-json.ts +++ b/packages/codemod/src/helpers/actions/migrate/migrate-json.ts @@ -3,6 +3,8 @@ import type {SAFE_ANY} from '@helpers/type'; 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'; const DEFAULT_INDENT = 2; @@ -13,17 +15,42 @@ export function detectIndent(content: string): number { return match ? match[1]?.length || DEFAULT_INDENT : DEFAULT_INDENT; } +function filterHeroUiPkgs(pkgs: string[]) { + return pkgs.filter((pkg) => pkg.includes(HEROUI_PREFIX) || pkg.includes(NEXTUI_PREFIX)); +} + export async function migrateJson(files: string[]) { try { await Promise.all( - files.map((file) => { + files.map(async (file) => { const content = getStore(file, 'rawContent'); const dirtyFlag = content.includes(NEXTUI_PREFIX); if (dirtyFlag) { const replacedContent = content.replaceAll(NEXTUI_PREFIX, HEROUI_PREFIX); + const json = safeParseJson(replacedContent); + + try { + await Promise.all([ + ...filterHeroUiPkgs(Object.keys(json.dependencies)).map(async (key) => { + const version = await fetchPackageLatestVersion(key); + + json.dependencies[key] = version; + }), + ...filterHeroUiPkgs(Object.keys(json.devDependencies)).map(async (key) => { + const version = await fetchPackageLatestVersion(key); + + json.devDependencies[key] = version; + }) + ]); + } catch (error) { + Logger.warn( + `Migrate ${file} failed\n${error}\nYou need to manually migrate the rest of the packages.` + ); + } + const indent = detectIndent(content); - writeFileAndUpdateStore(file, 'rawContent', replacedContent); + writeFileAndUpdateStore(file, 'rawContent', JSON.stringify(json, null, indent)); } }) ); diff --git a/packages/codemod/src/helpers/https.ts b/packages/codemod/src/helpers/https.ts new file mode 100644 index 0000000..f77bbbd --- /dev/null +++ b/packages/codemod/src/helpers/https.ts @@ -0,0 +1,61 @@ +import chalk from 'chalk'; +import ora from 'ora'; + +export async function fetchPackageLatestVersion(packageName: string): Promise { + const text = `Fetching ${packageName} version`; + const spinner = ora({ + discardStdin: false, + spinner: { + frames: [ + `⠋ ${chalk.gray(`${text}.`)}`, + `⠙ ${chalk.gray(`${text}..`)}`, + `⠹ ${chalk.gray(`${text}...`)}`, + `⠸ ${chalk.gray(`${text}.`)}`, + `⠼ ${chalk.gray(`${text}..`)}`, + `⠴ ${chalk.gray(`${text}...`)}`, + `⠦ ${chalk.gray(`${text}.`)}`, + `⠧ ${chalk.gray(`${text}..`)}`, + `⠇ ${chalk.gray(`${text}...`)}`, + `⠏ ${chalk.gray(`${text}.`)}` + ], + interval: 150 + } + }); + + spinner.start(); + + try { + const controller = new AbortController(); + const timeoutId = setTimeout(() => controller.abort(), 15000); // 15 seconds timeout + + 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}`); + } + + const data = await response.json(); + + return (data as {version: string}).version; + } 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.'); + } + throw error; + } + throw new Error('Parse version info failed'); + } finally { + spinner.stop(); + } +} diff --git a/packages/codemod/src/helpers/parse.ts b/packages/codemod/src/helpers/parse.ts index ef9997d..269c65e 100644 --- a/packages/codemod/src/helpers/parse.ts +++ b/packages/codemod/src/helpers/parse.ts @@ -49,3 +49,11 @@ export function parseContent(path: string): Collection | undefined { return; } } + +export function safeParseJson(content: string) { + try { + return JSON.parse(content); + } catch { + return {}; + } +}