diff --git a/src/commands/add.ts b/src/commands/add.ts index ab2412a..68b4eac 100644 --- a/src/commands/add.ts +++ b/src/commands/add.ts @@ -18,9 +18,10 @@ import { execa } from "execa"; import ora from "ora"; import prompts from "prompts"; import { z } from "zod"; +import { downloadHook } from "../utils/downloadHook"; const addOptionsSchema = z.object({ - components: z.array(z.string()).optional(), + hooks: z.array(z.string()).optional(), yes: z.boolean(), overwrite: z.boolean(), cwd: z.string(), @@ -30,8 +31,8 @@ const addOptionsSchema = z.object({ export const add = new Command() .name("add") - .description("add a component to your project") - .argument("[components...]", "the components to add") + .description("add a hook to your project") + .argument("[hooks...]", "the hooks to add") .option("-y, --yes", "skip confirmation prompt.", true) .option("-o, --overwrite", "overwrite existing files.", false) .option( @@ -39,180 +40,11 @@ export const add = new Command() "the working directory. defaults to the current directory.", process.cwd() ) - .option("-a, --all", "add all available components", false) - .option("-p, --path ", "the path to add the component to.") - .action(async (components, opts) => { - try { - const options = addOptionsSchema.parse({ - components, - ...opts, - }); - - const cwd = path.resolve(options.cwd); - - if (!existsSync(cwd)) { - logger.error(`The path ${cwd} does not exist. Please try again.`); - process.exit(1); - } - - const config = await getConfig(cwd); - if (!config) { - logger.warn( - `Configuration is missing. Please run ${chalk.green( - `init` - )} to create a components.json file.` - ); - process.exit(1); - } - - const registryIndex = await getRegistryIndex(); - - let selectedComponents = options.all - ? registryIndex.map((entry) => entry.name) - : options.components; - if (!options.components?.length && !options.all) { - const { components } = await prompts({ - type: "multiselect", - name: "components", - message: "Which components would you like to add?", - hint: "Space to select. A to toggle all. Enter to submit.", - instructions: false, - choices: registryIndex.map((entry) => ({ - title: entry.name, - value: entry.name, - selected: options.all - ? true - : options.components?.includes(entry.name), - })), - }); - selectedComponents = components; - } - - if (!selectedComponents?.length) { - logger.warn("No components selected. Exiting."); - process.exit(0); - } - - const tree = await resolveTree(registryIndex, selectedComponents); - const payload = await fetchTree(config.style, tree); - const baseColor = await getRegistryBaseColor(config.tailwind.baseColor); - - if (!payload.length) { - logger.warn("Selected components not found. Exiting."); - process.exit(0); - } - - if (!options.yes) { - const { proceed } = await prompts({ - type: "confirm", - name: "proceed", - message: `Ready to install components and dependencies. Proceed?`, - initial: true, - }); - - if (!proceed) { - process.exit(0); - } - } - - const spinner = ora(`Installing components...`).start(); - for (const item of payload) { - spinner.text = `Installing ${item.name}...`; - const targetDir = await getItemTargetPath( - config, - item, - options.path ? path.resolve(cwd, options.path) : undefined - ); - - if (!targetDir) { - continue; - } - - if (!existsSync(targetDir)) { - await fs.mkdir(targetDir, { recursive: true }); - } - - const existingComponent = item.files.filter((file) => - existsSync(path.resolve(targetDir, file.name)) - ); - - if (existingComponent.length && !options.overwrite) { - if (selectedComponents.includes(item.name)) { - spinner.stop(); - const { overwrite } = await prompts({ - type: "confirm", - name: "overwrite", - message: `Component ${item.name} already exists. Would you like to overwrite?`, - initial: false, - }); - - if (!overwrite) { - logger.info( - `Skipped ${item.name}. To overwrite, run with the ${chalk.green( - "--overwrite" - )} flag.` - ); - continue; - } - - spinner.start(`Installing ${item.name}...`); - } else { - continue; - } - } - - for (const file of item.files) { - let filePath = path.resolve(targetDir, file.name); - - // Run transformers. - const content = await transform({ - filename: file.name, - raw: file.content, - config, - baseColor, - }); - - if (!config.tsx) { - filePath = filePath.replace(/\.tsx$/, ".jsx"); - filePath = filePath.replace(/\.ts$/, ".js"); - } - - await fs.writeFile(filePath, content); - } - - const packageManager = await getPackageManager(cwd); - - // Install dependencies. - if (item.dependencies?.length) { - await execa( - packageManager, - [ - packageManager === "npm" ? "install" : "add", - ...item.dependencies, - ], - { - cwd, - } - ); - } - - // Install devDependencies. - if (item.devDependencies?.length) { - await execa( - packageManager, - [ - packageManager === "npm" ? "install" : "add", - "-D", - ...item.devDependencies, - ], - { - cwd, - } - ); - } - } - spinner.succeed(`Done.`); - } catch (error) { - handleError(error); + .option("-a, --all", "add all available hooks", false) + .option("-p, --path ", "the path to add the hook to.") + .action(async (hooks, opts) => { + for (let i = 0; i < hooks.length; i++) { + const hook = hooks[i]; + await downloadHook(hook); } }); diff --git a/src/utils/downloadHook.ts b/src/utils/downloadHook.ts new file mode 100644 index 0000000..b1b1b65 --- /dev/null +++ b/src/utils/downloadHook.ts @@ -0,0 +1,22 @@ +import fs from "fs/promises"; +import fetch from "node-fetch"; + +export const downloadHook = async (hookName: string) => { + const hookUrl = `https://raw.githubusercontent.com/novajslabs/nova.js/main/src/hooks/${hookName}.ts`; + + try { + const response = await fetch(hookUrl); + + if (!response.ok) { + throw Error; + } + + const arrayBuffer = await response.arrayBuffer(); + const buffer = Buffer.from(arrayBuffer); + + await fs.writeFile(`${hookName}.ts`, buffer); + console.log(`✅ ${hookName} added`); + } catch (e) { + console.log(`❌ ${hookName} error`); + } +};