Skip to content

Commit

Permalink
Finish feature/1
Browse files Browse the repository at this point in the history
  • Loading branch information
dlcastillop authored Jun 5, 2024
2 parents cb6461d + 255aa38 commit 18ef771
Show file tree
Hide file tree
Showing 2 changed files with 32 additions and 178 deletions.
188 changes: 10 additions & 178 deletions src/commands/add.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
Expand All @@ -30,189 +31,20 @@ 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(
"-c, --cwd <cwd>",
"the working directory. defaults to the current directory.",
process.cwd()
)
.option("-a, --all", "add all available components", false)
.option("-p, --path <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 <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);
}
});
22 changes: 22 additions & 0 deletions src/utils/downloadHook.ts
Original file line number Diff line number Diff line change
@@ -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`);
}
};

0 comments on commit 18ef771

Please sign in to comment.