Skip to content

Commit

Permalink
feat: add nextui env command (#6)
Browse files Browse the repository at this point in the history
* feat: add nextui env command

* fix: current fn error

* fix: json read and ts error

* feat: add new version show

* fix: rename error
  • Loading branch information
winchesHe committed Mar 30, 2024
1 parent 9ce706e commit 16820f4
Show file tree
Hide file tree
Showing 14 changed files with 278 additions and 143 deletions.
19 changes: 19 additions & 0 deletions src/actions/env-action.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { outputComponents, outputInfo } from '@helpers/output-info';
import { getPackageInfo } from '@helpers/package';
import { resolver } from 'src/constants/path';

interface EnvActionOptions {
packagePath?: string;
}

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

const { currentComponents } = await getPackageInfo(packagePath);

/** ======================== Output the current components ======================== */
outputComponents(currentComponents);

/** ======================== Output the system environment info ======================== */
outputInfo();
}
4 changes: 2 additions & 2 deletions src/actions/init-action.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ export async function initAction(projectName: string, options: InitActionOptions
async function generateTemplate(url: string) {
await oraPromise(downloadTemplate(ROOT, url), {
failText(error) {
Logger.error(`NextUI CLI downloadTemplate Error: ${error}`);
Logger.prefix('error', `downloadTemplate Error: ${error}`);
process.exit(1);
},
successText: (() => {
Expand All @@ -81,7 +81,7 @@ async function generateTemplate(url: string) {
function renameTemplate(originName: string, projectName: string) {
rename(`${ROOT}/${originName}`, `${ROOT}/${projectName}`, (err) => {
if (err) {
Logger.warn(`NextUI CLI rename Error: ${err}`);
Logger.prefix('warn', `rename Error: ${err}`);
}
});
}
122 changes: 9 additions & 113 deletions src/actions/list-action.ts
Original file line number Diff line number Diff line change
@@ -1,50 +1,26 @@
import { resolve } from 'path';

import chalk from 'chalk';

import { Logger } from '@helpers/logger';
import { PasCalCase } from '@helpers/utils';
import { outputComponents } from '@helpers/output-info';
import { getPackageInfo } from '@helpers/package';

import {
type NextUIComponents,
colorNextUIComponentKeys,
nextUIComponents,
orderNextUIComponentKeys
} from '../../src/constants/component';
import { ROOT } from '../../src/constants/path';
import { type NextUIComponents, nextUIComponents } from '../../src/constants/component';
import { resolver } from '../../src/constants/path';

interface ListActionOptions {
current?: boolean;
packagePath?: string;
}

const rounded = {
bl: '╰',
br: '╯',
h: '─',
tl: '╭',
tr: '╮',
v: '│'
} as const;
const space = ' ';
const padStart = `${rounded.v}${space}`;
const padEnd = `${space}${rounded.v}${space}`;

export async function listAction(options: ListActionOptions) {
const { current, packagePath = resolve(ROOT, 'package.json') } = options;
const { current, packagePath = resolver('package.json') } = options;

let components = nextUIComponents as NextUIComponents;

try {
/** ======================== Get the installed components ======================== */
if (current) {
const pkg = await import(packagePath);
const devDependencies = pkg.devDependencies || {};
const dependencies = pkg.dependencies || {};
const allDependencies = { ...devDependencies, ...dependencies };
const dependenciesKeys = new Set(Object.keys(allDependencies));
const { currentComponents } = await getPackageInfo(packagePath);

components = components.filter((component) => dependenciesKeys.has(component.name));
components = currentComponents;
}

if (!components.length) {
Expand All @@ -56,88 +32,8 @@ export async function listAction(options: ListActionOptions) {
}

/** ======================== Output the components ======================== */
outputComponents(components);
current ? outputComponents(components) : outputComponents(components, 'list');
} catch (error) {
Logger.error(`NextUI CLI Error occurred while listing the components: ${error}`);
}
}

function outputComponents(components: NextUIComponents) {
const componentKeyLengthMap: Record<keyof NextUIComponents[0], number> = {
description: 0,
docs: 0,
name: 0,
package: 0,
status: 0,
version: 0
};

for (const component of components) {
for (const key in component) {
// Align the length of the version
componentKeyLengthMap[key] = Math.max(
componentKeyLengthMap[key],
key === 'version' ? 'version'.length : String(component[key]).length
);
}
}

let transformComponentsOutput = components.reduce((acc, component) => {
let outputData = padStart;

for (const key of orderNextUIComponentKeys) {
let value = component[key].padEnd(componentKeyLengthMap[key]);

if (component.status === 'stable' && colorNextUIComponentKeys.includes(key)) {
value = chalk.greenBright(value);
} else if (component.status === 'newPost') {
value = chalk.magentaBright(value);
} else if (component.status === 'updated') {
value = chalk.blueBright(value);
}

outputData += value + padEnd;
}

outputData;

acc.push(outputData);

return acc;
}, [] as string[]);

/** ======================== Generate box header ======================== */
let boxHeader = rounded.tl + padStart.replace(/.*/g, rounded.h).slice(1);
let boxHeaderSec = padStart;
let boxHeaderTrd = rounded.v + padStart.replace(/.*/g, rounded.h).slice(1);

for (const key of orderNextUIComponentKeys) {
boxHeader += `${rounded.h.padEnd(componentKeyLengthMap[key] + 7, rounded.h)}`;
boxHeaderSec += chalk.redBright(PasCalCase(key).padEnd(componentKeyLengthMap[key])) + padEnd;
boxHeaderTrd += `${rounded.h.padEnd(componentKeyLengthMap[key] + 7, rounded.h)}`;
}

boxHeader = boxHeader.slice(0, -2) + rounded.tr;
boxHeaderTrd = boxHeaderTrd.slice(0, -2) + rounded.v;

/** ======================== Generate box footer ======================== */
let boxFooter = rounded.bl + padStart.replace(/.*/g, rounded.h).slice(1);

for (const key of orderNextUIComponentKeys) {
boxFooter += `${rounded.h.padEnd(componentKeyLengthMap[key] + 7, rounded.h)}`;
Logger.prefix('error', `Error occurred while listing the components: ${error}`);
}

boxFooter = boxFooter.slice(0, -2) + rounded.br;

transformComponentsOutput = [
boxHeader,
boxHeaderSec,
boxHeaderTrd,
...transformComponentsOutput,
boxFooter
];

Logger.info('Current NextUI Components:\n');

Logger.log(transformComponentsOutput.join('\n'));
}
6 changes: 4 additions & 2 deletions src/constants/component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -320,12 +320,14 @@ export const nextUIComponents = [
}
];

export const orderNextUIComponentKeys = ['package', 'version', 'status', 'docs'];
export const orderNextUIComponentKeys = ['package', 'version', 'status', 'docs'] as const;

export const colorNextUIComponentKeys = ['package', 'version', 'status'];

export type NextUIComponentStatus = 'stable' | 'updated' | 'newPost';

export type NextUIComponents = (Omit<(typeof nextUIComponents)[0], 'status'> & {
type NextUIComponent = (typeof nextUIComponents)[0];

export type NextUIComponents = (Omit<NextUIComponent, 'status'> & {
status: NextUIComponentStatus;
})[];
3 changes: 3 additions & 0 deletions src/constants/path.ts
Original file line number Diff line number Diff line change
@@ -1 +1,4 @@
import { resolve } from 'path';

export const ROOT = process.cwd();
export const resolver = (path: string) => resolve(ROOT, path);
9 changes: 9 additions & 0 deletions src/helpers/fetch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ import { pipeline } from 'stream/promises';
import retry from 'async-retry';
import tar from 'tar';

/**
* Fetch the tar stream from the specified URL.
* @param url
*/
async function fetchTarStream(url: string) {
const res = await fetch(url);

Expand All @@ -14,6 +18,11 @@ async function fetchTarStream(url: string) {
return Readable.fromWeb(res.body);
}

/**
* Download the template from the specified URL and extract it to the specified directory.
* @param root
* @param url
*/
export async function downloadTemplate(root: string, url: string) {
await retry(
async (bail) => {
Expand Down
9 changes: 9 additions & 0 deletions src/helpers/logger.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
/* eslint-disable no-console */

import type { SAFE_ANY } from './type';

import chalk from 'chalk';
import { default as _gradientString } from 'gradient-string';

export const defaultColors = ['#FF1CF7', '#B249F8'] as const;

export const gradientString = _gradientString;

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

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

Expand All @@ -33,6 +38,10 @@ export class Logger {
this.log(gradientString(...(options?.colors ?? defaultColors))(String(content)));
}

static prefix(type: PrefixLogType, ...args: SAFE_ANY) {
return this[type](logPrefix, ...args);
}

static newLine(lines?: number) {
if (!lines) lines = 1;

Expand Down
Loading

0 comments on commit 16820f4

Please sign in to comment.