diff --git a/packages/gluestack-cli/src/commands/add.ts b/packages/gluestack-cli/src/commands/add.ts index e551d954..b2fa335b 100644 --- a/packages/gluestack-cli/src/commands/add.ts +++ b/packages/gluestack-cli/src/commands/add.ts @@ -4,7 +4,7 @@ import os from 'os'; import { join } from 'path'; import { handleError } from '../util/handle-error'; import { log } from '@clack/prompts'; -import { componentAdder, hookAdder, isHookFromConfig } from '../util/add'; +import { componentAdder } from '../util/add'; import { config } from '../config'; import { checkWritablePath, @@ -18,7 +18,7 @@ import { checkIfInitialized, getComponentsPath } from '../util/config'; const _homeDir = os.homedir(); const addOptionsSchema = z.object({ - components: z.string().optional(), + components: z.array(z.string()), all: z.boolean(), useNpm: z.boolean(), useYarn: z.boolean(), @@ -39,19 +39,13 @@ export const add = new Command() .option('--path ', 'path to the components directory') .action(async (components, opts, command) => { try { - if (command.args.length > 1) { - log.error( - '\x1b[31mOnly one component can be provided at a time, please provide the component name you want to add or --all.\x1b[0m' - ); - process.exit(1); - } const options = addOptionsSchema.parse({ - components: components ?? '', + components: command.args.length > 0 ? command.args : [], ...opts, }); if ( - options.all === false && - (options.components === '' || options.components === undefined) + (!options.all && options.components?.length === 0) || + (options.all && options.components?.length > 0) ) { log.error( '\x1b[31mInvalid arguement, please provide the component/hook name you want to add or --all.\x1b[0m' @@ -94,24 +88,15 @@ export const add = new Command() config.writableComponentsPath = options.path; } await cloneRepositoryAtRoot(join(_homeDir, config.gluestackDir)); + // define args based on --all or components + const args = options.all + ? { addAll: true } + : { componentArgs: options.components.map((c) => c.toLowerCase()) }; - if (options.all) { - try { - await componentAdder({ - requestedComponent: '--all', - }); - } catch (err) { - log.error(`\x1b[31mError: ${(err as Error).message}\x1b[0m`); - } - } else if (await isHookFromConfig(options.components)) { - options.components && - (await hookAdder({ - requestedHook: options.components, - })); - } else { - await componentAdder({ - requestedComponent: options.components?.toLowerCase(), - }); + try { + await componentAdder(args); + } catch (err) { + log.error(`\x1b[31mError: ${(err as Error).message}\x1b[0m`); } } catch (err) { handleError(err); diff --git a/packages/gluestack-cli/src/util/add/index.ts b/packages/gluestack-cli/src/util/add/index.ts index 6b0a91ea..5a0741ff 100644 --- a/packages/gluestack-cli/src/util/add/index.ts +++ b/packages/gluestack-cli/src/util/add/index.ts @@ -1,4 +1,5 @@ import fs from 'fs-extra'; +import chalk from 'chalk'; import os from 'os'; import { basename, join, parse } from 'path'; import { log, confirm } from '@clack/prompts'; @@ -15,56 +16,65 @@ const _homeDir = os.homedir(); let existingComponentsChecked: boolean = false; const componentAdder = async ({ - requestedComponent = '', + addAll = false, + componentArgs = [], showWarning = true, +}: { + addAll?: boolean; + componentArgs?: Array; + showWarning?: boolean; }) => { try { - console.log(`\n\x1b[1mAdding new component...\x1b[0m\n`); - let hooksToAdd: string[] = []; - if ( - requestedComponent && - requestedComponent !== '--all' && - !(await checkIfComponentIsValid(requestedComponent)) - ) { - log.error( - `The ${requestedComponent} does not exist. Kindly choose a valid component name.` - ); - return; - } - let requestedComponents = - requestedComponent === '--all' - ? await getAllComponents() - : [requestedComponent]; - - const { hooks } = await checkComponentDependencies(requestedComponents); - hooksToAdd = Array.from(hooks); - - const updatedComponents = - !existingComponentsChecked && showWarning && requestedComponent - ? await isComponentInProject(requestedComponents) - : requestedComponents; - const count = updatedComponents.length; - await Promise.all( - updatedComponents.map(async (component) => { - const targetPath = join( - projectRootPath, - config.writableComponentsPath, - component + const res = await sortComponentsAndHooks(componentArgs); + let componentsToAdd = res.components; + let hooksToAdd = res.hooks; + if (componentsToAdd.length > 0 || addAll) { + if ( + !addAll && + componentsToAdd?.length && + !(await checkIfComponentIsValid(componentsToAdd)) + ) { + log.error( + chalk.red( + `Invalid names entered. Kindly check and choose a valid component name.` + ) ); + return; + } + console.log(`\n\x1b[1mAdding new component...\x1b[0m\n`); + let requestedComponents = addAll + ? await getAllComponents() + : componentsToAdd; + const { hooks } = await checkComponentDependencies(requestedComponents); + hooksToAdd = Array.from(hooks); - await writeComponent(component, targetPath); - }) - ) - .then(async () => { - await installDependencies(updatedComponents); - log.success( - `\x1b[32mDone!\x1b[0m Added new \x1b[1mgluestack-ui\x1b[0m ${count === 1 ? 'component' : 'components'} into project` - ); - }) - .catch((err) => { - log.error(`\x1b[31mError : ${(err as Error).message}\x1b[0m`); - }); - if (hooksToAdd.length > 0) await hookAdder({ requestedHook: hooksToAdd }); + const updatedComponents = + !existingComponentsChecked && showWarning && componentsToAdd.length + ? await isComponentInProject(requestedComponents) + : requestedComponents; + const count = updatedComponents.length; + await Promise.all( + updatedComponents.map(async (component) => { + const targetPath = join( + projectRootPath, + config.writableComponentsPath, + component + ); + + await writeComponent(component, targetPath); + }) + ) + .then(async () => { + await installDependencies(updatedComponents); + log.success( + `\x1b[32mDone!\x1b[0m Added new \x1b[1mgluestack-ui\x1b[0m ${count === 1 ? 'component' : 'components'} into project` + ); + }) + .catch((err) => { + log.error(`\x1b[31mError : ${(err as Error).message}\x1b[0m`); + }); + } + if (hooksToAdd.length > 0) await hookAdder(hooksToAdd); } catch (err) { log.error(`\x1b[31mError: ${(err as Error).message}\x1b[0m`); } @@ -121,9 +131,11 @@ const processTerminate = (message: string) => { process.exit(1); }; -const checkIfComponentIsValid = async (component: string): Promise => { +const checkIfComponentIsValid = async ( + components: string[] +): Promise => { const componentList = await getAllComponents(); - if (componentList.includes(component) || componentList.includes(component)) + if (components.every((component) => componentList.includes(component))) return true; else return false; }; @@ -165,11 +177,7 @@ const confirmOverride = async ( return shouldContinue; }; -const hookAdder = async ({ - requestedHook, -}: { - requestedHook: string | string[]; -}) => { +const hookAdder = async (requestedHook: string[]) => { try { console.log(`\n\x1b[1mAdding new hook...\x1b[0m\n`); await writeHook(requestedHook); @@ -181,12 +189,36 @@ const hookAdder = async ({ } }; -const isHookFromConfig = async (hook: string | undefined): Promise => { +const sortComponentsAndHooks = async ( + inputNames: string[] | undefined +): Promise<{ hooks: string[]; components: string[] }> => { + if (!inputNames || inputNames.length === 0) { + return { hooks: [], components: [] }; + } + + const hooksPath = join( + _homeDir, + config.gluestackDir, + config.hooksResourcePath + ); const hooksList = fs - .readdirSync(join(_homeDir, config.gluestackDir, config.hooksResourcePath)) - .map((file) => removeHyphen(parse(file).name)); - if (hook && hooksList.includes(hook.toLowerCase())) return true; - else return false; + .readdirSync(hooksPath) + .map((file) => removeHyphen(parse(file).name).toLowerCase()); + + const result = inputNames.reduce( + (acc, name) => { + const lowercaseName = name.toLowerCase(); + if (hooksList.includes(lowercaseName)) { + acc.hooks.push(name); + } else { + acc.components.push(name); + } + return acc; + }, + { hooks: [] as string[], components: [] as string[] } + ); + + return result; }; const hookFileName = async (hook: string): Promise => { @@ -201,8 +233,7 @@ const hookFileName = async (hook: string): Promise => { }); return fileName; }; -const writeHook = async (hooks: string | string[]) => { - const hooksArray = Array.isArray(hooks) ? hooks : [hooks]; +const writeHook = async (hooksArray: string[]) => { for (const hook of hooksArray) { const fileName = await hookFileName(hook); const utilsPath = join( @@ -243,4 +274,4 @@ const confirmHookOverride = async (hook: string): Promise => { return shouldContinue; }; -export { componentAdder, getAllComponents, isHookFromConfig, hookAdder }; +export { componentAdder, getAllComponents }; diff --git a/packages/gluestack/src/v2.ts b/packages/gluestack/src/v2.ts index 759cf035..e2e4061e 100644 --- a/packages/gluestack/src/v2.ts +++ b/packages/gluestack/src/v2.ts @@ -62,6 +62,7 @@ async function createProject(createOptions: ProjectOptions) { : `npx create-next-app@latest ${projectName} --ts --no-eslint --use-${packageManager} --import-alias "@/*" --no-tailwind --no-src-dir --app`; } else if (projectType.includes('react-native')) { // create react-native project + message = `⏳ Creating a react-native-cli project. Hang tight, this may take a bit...`; const useCocoapods = router.includes('react-native-cli-cocoapods') ? true : false;