Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add format effected files #134

Merged
merged 5 commits into from
Jan 14, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions packages/codemod/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ The CLI provides a comprehensive suite of tools to migrate your codebase from Ne
## 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:

Expand Down Expand Up @@ -49,9 +51,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
Expand Down Expand Up @@ -141,7 +144,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:
Expand Down
26 changes: 25 additions & 1 deletion packages/codemod/src/actions/migrate-action.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,16 @@ import chalk from 'chalk';
import {confirmClack} from 'src/prompts/clack';

import {NEXTUI_PREFIX} 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 {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, getStore, storeParsedContent, storePathsRawContent} from '../helpers/store';
import {transformPaths} from '../helpers/transform';
import {getCanRunCodemod} from '../helpers/utils';

Expand Down Expand Up @@ -144,5 +146,27 @@ export async function migrateAction(projectPaths?: string[], options = {} as Mig
step++;
}

const format = getOptionsValue('format');
/** ======================== 7. 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();
}

p.outro(chalk.green('βœ… Migration completed!'));
}
10 changes: 10 additions & 0 deletions packages/codemod/src/helpers/actions/lint-affected-files.ts
Original file line number Diff line number Diff line change
@@ -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;
}
}
Original file line number Diff line number Diff line change
@@ -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) {
Expand All @@ -13,6 +13,7 @@ export function migrateCssVariables(files: string[]) {
);

writeFileAndUpdateStore(file, 'rawContent', content);
updateAffectedFiles(file);
}
}
}
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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 {}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ 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;

Expand Down Expand Up @@ -51,6 +51,7 @@ export async function migrateJson(files: string[]) {
const indent = detectIndent(content);

writeFileAndUpdateStore(file, 'rawContent', JSON.stringify(json, null, indent));
updateAffectedFiles(file);
}
})
);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {HEROUI_PROVIDER, NEXTUI_PROVIDER} from '../../../constants/prefix';
import {getStore, writeFileAndUpdateStore} from '../../store';
import {getStore, updateAffectedFiles, writeFileAndUpdateStore} from '../../store';

import {migrateByRegex} from './migrate-common';

Expand All @@ -26,6 +26,7 @@ export function migrateNextuiProvider(paths: string[]) {
if (dirtyFlag) {
// Write the modified content back to the file
writeFileAndUpdateStore(path, 'rawContent', rawContent);
updateAffectedFiles(path);
}
// eslint-disable-next-line no-empty
} catch {}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -56,6 +56,7 @@ export function migrateTailwindcss(paths: string[]) {

if (dirtyFlag) {
writeFileAndUpdateStore(path, 'parsedContent', parsedContent);
updateAffectedFiles(path);
}
}
}
58 changes: 58 additions & 0 deletions packages/codemod/src/helpers/lint.ts
Original file line number Diff line number Diff line change
@@ -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;
}
}
17 changes: 17 additions & 0 deletions packages/codemod/src/helpers/options.ts
Original file line number Diff line number Diff line change
@@ -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<T extends keyof typeof CODEMOD_OPTIONS>(key: T) {
return CODEMOD_OPTIONS[key];
}
6 changes: 6 additions & 0 deletions packages/codemod/src/helpers/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,3 +100,9 @@ export function writeFileAndUpdateStore<K extends ExcludeStoreKey>(
updateStore(path, key, value);
key === 'parsedContent' && updateStore(path, 'rawContent', data.rawContent);
}

export const affectedFiles = new Set<string>();

export function updateAffectedFiles(path: string) {
affectedFiles.add(path);
}
5 changes: 5 additions & 0 deletions packages/codemod/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 nextui = new Command();
Expand All @@ -22,6 +23,7 @@ nextui
.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);

nextui
Expand All @@ -33,6 +35,9 @@ nextui
nextui.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;
});
Expand Down
Loading