Skip to content

Commit

Permalink
feat: doctor command add check redundant and required denpendencies
Browse files Browse the repository at this point in the history
  • Loading branch information
winchesHe committed Mar 30, 2024
1 parent b95ebdf commit 7ffd6a9
Show file tree
Hide file tree
Showing 8 changed files with 154 additions and 5 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@
- [ ] Add `nextui doctor` command

> Check whether installed redundant dependencies.
> Check whether the required dependence has been installed.
> Check tailwind config、provide has been added.
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"license": "MIT",
"version": "0.1.0",
"homepage": "https://github.com/nextui-org/nextui-cli#readme",
"description": "A CLI tool that unlocks seamless NextUI integration.",
"description": "A CLI tool that unlocks seamless NextUI integration",
"keywords": [
"UI",
"CLI",
Expand Down
93 changes: 93 additions & 0 deletions src/actions/doctor-action.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import chalk from 'chalk';

import { checkRequiredContentInstalled } from '@helpers/check';
import { Logger, type PrefixLogType } from '@helpers/logger';
import { getPackageInfo } from '@helpers/package';
import { resolver } from 'src/constants/path';

interface DoctorActionOptions {
packagePath?: string;
}

interface ProblemRecord {
name: string;
level: Extract<PrefixLogType, 'error' | 'warn'>;
outputFn: () => void;
}

export async function doctorAction(options: DoctorActionOptions) {
const { packagePath = resolver('package.json') } = options;

const { allDependenciesKeys, currentComponents, isAllComponents } =
await getPackageInfo(packagePath);

/** ======================== Problem record ======================== */
const problemRecord: ProblemRecord[] = [];

/** ======================== Check whether installed redundant dependencies ======================== */
if (isAllComponents && currentComponents.length) {
problemRecord.push({
level: 'warn',
name: 'redundantDependencies',
outputFn: () => {
Logger.warn('you have installed redundant dependencies, please remove them');
Logger.newLine();
Logger.info('The redundant dependencies are:');
Logger.newLine();
currentComponents.forEach((component) => {
Logger.info(`- ${component.package}`);
});
}
});
}

/** ======================== Check if the allComponents required dependencies installed ======================== */
if (isAllComponents) {
// Check if framer-motion allComponents is installed
const [isCorrectInstalled, ...missingDependencies] = checkRequiredContentInstalled(
'all',
allDependenciesKeys
);

if (!isCorrectInstalled) {
problemRecord.push({
level: 'error',
name: 'missingDependencies',
outputFn: () => {
Logger.error('you have not installed the required dependencies');
Logger.newLine();
Logger.info('The required dependencies are:');
Logger.newLine();
missingDependencies.forEach((dependency) => {
Logger.info(`- ${dependency}`);
});
}
});
}
}

/** ======================== Return when there is no problem ======================== */
if (!problemRecord.length) {
Logger.success('🌟Congratulation no problem found in your project');

return;
}

/** ======================== Output the problem record ======================== */
Logger.prefix(
'error',
`❌Sorry there are ${chalk.underline(problemRecord.length)} problem${
problemRecord.length === 1 ? '' : 's'
} in your project`
);
Logger.newLine();

for (let index = 0; index < problemRecord.length; index++) {
const problem = problemRecord[index] as ProblemRecord;

Logger[problem.level](`❗️Problem ${index + 1}: ${chalk.bold(problem.name)}`);
Logger.newLine();
problem.outputFn();
Logger.newLine();
}
}
3 changes: 3 additions & 0 deletions src/constants/required.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export const ALL_COMPONENTS = '@nextui-org/react';
export const FRAMER_MOTION = 'framer-motion';
export const ALL_COMPONENTS_REQUIRED = [ALL_COMPONENTS, FRAMER_MOTION] as const;
34 changes: 34 additions & 0 deletions src/helpers/check.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import type { SAFE_ANY } from './type';

import { ALL_COMPONENTS, FRAMER_MOTION } from 'src/constants/required';

export type CheckType = 'all' | 'partial';

type CheckResult<T extends SAFE_ANY[] = SAFE_ANY[]> = [boolean, ...T];
/**
* Check if the required content is installed
* @example return result and missing required [false, '@nextui-org/react', 'framer-motion']
* @param type
* @param dependenciesKeys
* @returns
*/
export function checkRequiredContentInstalled(
type: CheckType,
dependenciesKeys: Set<string>
): CheckResult {
if (type === 'all') {
const hasAllComponents = dependenciesKeys.has(ALL_COMPONENTS);
const hasFramerMotion = dependenciesKeys.has(FRAMER_MOTION);
const result = [] as unknown as CheckResult;

if (hasAllComponents && hasFramerMotion) {
return [true];
}
!hasAllComponents && result.push(ALL_COMPONENTS);
!hasFramerMotion && result.push(FRAMER_MOTION);

return [false, ...result];
}

return [false];
}
9 changes: 8 additions & 1 deletion src/helpers/logger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,10 @@ export const gradientString = _gradientString;

const logPrefix = gradientString(...defaultColors)('NextUI CLI:');

type PrefixLogType = Extract<keyof typeof Logger, 'error' | 'gradient' | 'info' | 'log' | 'warn'>;
export type PrefixLogType = Extract<
keyof typeof Logger,
'error' | 'gradient' | 'info' | 'log' | 'warn' | 'success'
>;
export class Logger {
constructor() {}

Expand All @@ -23,6 +26,10 @@ export class Logger {
console.info(...args.map((item) => chalk.blue(item)));
}

static success(...args: Parameters<typeof console.info>) {
console.info(...args.map((item) => chalk.green(item)));
}

static warn(...args: Parameters<typeof console.warn>) {
console.warn(...args.map((item) => chalk.yellow(item)));
}
Expand Down
9 changes: 6 additions & 3 deletions src/helpers/package.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { readFileSync } from 'fs';

import { type NextUIComponents, nextUIComponents } from 'src/constants/component';
import { ALL_COMPONENTS } from 'src/constants/required';

import { Logger } from './logger';

Expand All @@ -20,11 +21,11 @@ export async function getPackageInfo(packagePath: string) {
const devDependencies = pkg.devDependencies || {};
const dependencies = pkg.dependencies || {};
const allDependencies = { ...devDependencies, ...dependencies };
const dependenciesKeys = new Set(Object.keys(allDependencies));
const allDependenciesKeys = new Set(Object.keys(allDependencies));

const currentComponents = (nextUIComponents as unknown as NextUIComponents).filter(
(component) => {
if (dependenciesKeys.has(component.package)) {
if (allDependenciesKeys.has(component.package)) {
const currentVersion = allDependencies[component.package];

component.version = `${currentVersion} new: ${component.version}`;
Expand All @@ -35,13 +36,15 @@ export async function getPackageInfo(packagePath: string) {
return false;
}
) as NextUIComponents;
const isAllComponents = allDependenciesKeys.has(ALL_COMPONENTS);

return {
allDependencies,
allDependenciesKeys,
currentComponents,
dependencies,
dependenciesKeys,
devDependencies,
isAllComponents,
package: pkg
};
}
7 changes: 7 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { getCommandDescAndLog } from '@helpers/utils';

import pkg from '../package.json';

import { doctorAction } from './actions/doctor-action';
import { envAction } from './actions/env-action';
import { initAction } from './actions/init-action';
import { listAction } from './actions/list-action';
Expand Down Expand Up @@ -49,6 +50,12 @@ nextui
.option('-p --packagePath [string]', 'The path to the package.json file')
.action(envAction);

nextui
.command('doctor')
.description('Check whether exist problem in user project')
.option('-p --packagePath [string]', 'The path to the package.json file')
.action(doctorAction);

nextui.parseAsync(process.argv).catch(async (reason) => {
Logger.newLine();
Logger.error('Unexpected error. Please report it as a bug:');
Expand Down

0 comments on commit 7ffd6a9

Please sign in to comment.