Skip to content

Commit

Permalink
cli package
Browse files Browse the repository at this point in the history
  • Loading branch information
5rahim committed Jan 17, 2024
1 parent ad8ea5f commit f6cab64
Show file tree
Hide file tree
Showing 27 changed files with 4,489 additions and 223 deletions.
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -44,3 +44,9 @@ next-env.d.ts
cli/snapshot

test

dist

cli/node_modules
cli/.env
cli/src
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,3 +73,7 @@ Chalk UI is inspired by the following projects:
- [tailwind-merge](https://github.com/dcastil/tailwind-merge)
- [tailwindcss-animate](https://github.com/jamiebuilds/tailwindcss-animate)
- And many more...

## License

This project is licensed under the MIT License - see the [LICENSE](https://github.com/5rahim/chalk-ui/blob/main/LICENSE) file for details.
30 changes: 30 additions & 0 deletions cli/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<p align="center">
<img src="https://github.com/5rahim/chalk-ui/blob/main/public/images/logo.png?raw=true" alt="preview" width="75px"/>
</p>

<h2 align="center">Chalk UI</h2>

<h4 align="center">Opinionated and flexible React components, styled with TailwindCSS, built for Next.js SaaS applications.</h4>

## Usage

```bash
npx @rahimstack/chalk-ui@latest init
```

## Documentation

The documentation is available at [chalk.rahim.app](https://chalk.rahim.app/).

## Features

- Broad range of components tailored for SaaS applications
- Higher-level API for Radix UI primitives
- Consistent design language and API
- Usage of battle-tested libraries
- Customizable with TailwindCSS
- 100% TypeScript

## License

This project is licensed under the MIT License - see the [LICENSE](https://github.com/5rahim/chalk-ui/blob/main/LICENSE) file for details.
2 changes: 1 addition & 1 deletion cli/bank/bank.json
Original file line number Diff line number Diff line change
Expand Up @@ -227,7 +227,7 @@
{
"name": "icon-button.tsx",
"dir": "button",
"content": "import { cva, VariantProps } from \"class-variance-authority\"\r\nimport * as React from \"react\"\r\nimport { Button, ButtonProps } from \".\"\r\nimport { cn, defineStyleAnatomy } from \"../core/styling\"\r\n\r\n/* -------------------------------------------------------------------------------------------------\r\n * Anatomy\r\n * -----------------------------------------------------------------------------------------------*/\r\n\r\nexport const IconButtonAnatomy = defineStyleAnatomy({\r\n root: cva(\"UI-IconButton_root p-0 flex-none\", {\r\n variants: {\r\n size: {\r\n xs: \"text-xl h-6 w-6\",\r\n sm: \"text-xl h-8 w-8\",\r\n md: \"text-2xl h-10 w-10\",\r\n lg: \"text-3xl h-12 w-12\",\r\n xl: \"text-4xl h-14 w-14\",\r\n },\r\n },\r\n defaultVariants: {\r\n size: \"md\",\r\n },\r\n }),\r\n})\r\n\r\n/* -------------------------------------------------------------------------------------------------\r\n * IconButton\r\n * -----------------------------------------------------------------------------------------------*/\r\n\r\n\r\nexport type IconButtonProps = Omit<ButtonProps, \"leftIcon\" | \"rightIcon\" | \"iconSpacing\" | \"iconClass\" | \"children\"> &\r\n VariantProps<typeof IconButtonAnatomy.root> & {\r\n icon?: React.ReactNode\r\n}\r\n\r\nexport const IconButton = React.forwardRef<HTMLButtonElement, IconButtonProps>((props, ref) => {\r\n\r\n const {\r\n className,\r\n icon,\r\n size,\r\n loading,\r\n ...rest\r\n } = props\r\n\r\n return (\r\n <>\r\n <Button\r\n className={cn(\r\n IconButtonAnatomy.root({ size }),\r\n className,\r\n )}\r\n loading={loading}\r\n iconSpacing=\"0\"\r\n {...rest}\r\n ref={ref}\r\n >\r\n {!loading && icon}\r\n </Button>\r\n </>\r\n )\r\n\r\n})\r\n\r\nIconButton.displayName = \"IconButton\"\r\n"
"content": "import { cva, VariantProps } from \"class-variance-authority\"\r\nimport * as React from \"react\"\r\nimport { Button, ButtonProps } from \".\"\r\nimport { cn, defineStyleAnatomy } from \"../core/styling\"\r\n\r\n/* -------------------------------------------------------------------------------------------------\r\n * Anatomy\r\n * -----------------------------------------------------------------------------------------------*/\r\n\r\nexport const IconButtonAnatomy = defineStyleAnatomy({\r\n root: cva(\"UI-IconButton_root p-0 flex-none\", {\r\n variants: {\r\n size: {\r\n xs: \"text-xl h-6 w-6\",\r\n sm: \"text-xl h-8 w-8\",\r\n md: \"text-2xl h-10 w-10\",\r\n lg: \"text-3xl h-12 w-12\",\r\n xl: \"text-4xl h-14 w-14\",\r\n },\r\n },\r\n defaultVariants: {\r\n size: \"md\",\r\n },\r\n }),\r\n})\r\n\r\n/* -------------------------------------------------------------------------------------------------\r\n * IconButton\r\n * -----------------------------------------------------------------------------------------------*/\r\n\r\n\r\nexport type IconButtonProps = Omit<ButtonProps, \"leftIcon\" | \"rightIcon\" | \"iconSpacing\" | \"iconClass\" | \"children\"> &\r\n VariantProps<typeof IconButtonAnatomy.root> & {\r\n icon?: React.ReactNode\r\n}\r\n\r\nexport const IconButton = React.forwardRef<HTMLButtonElement, IconButtonProps>((props, ref) => {\r\n\r\n const {\r\n className,\r\n icon,\r\n size,\r\n loading,\r\n ...rest\r\n } = props\r\n\r\n return (\r\n <Button\r\n className={cn(\r\n IconButtonAnatomy.root({ size }),\r\n className,\r\n )}\r\n loading={loading}\r\n iconSpacing=\"0\"\r\n {...rest}\r\n ref={ref}\r\n >\r\n {!loading && icon}\r\n </Button>\r\n )\r\n\r\n})\r\n\r\nIconButton.displayName = \"IconButton\"\r\n"
},
{
"name": "index.tsx",
Expand Down
3 changes: 1 addition & 2 deletions cli/commands/add.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { Command } from "commander"

import { defaultUIFolder } from "../info"
import { logger } from "../utils/logger"
import prompts from "prompts"
Expand Down Expand Up @@ -39,7 +38,7 @@ export const add = new Command()
])

// Get available components
const availableComponents = getAvailableComponents()
const availableComponents = await getAvailableComponents()
if (!availableComponents?.length) {
logger.error("An error occurred while fetching components. Please try again.",)
process.exit(0)
Expand Down
4 changes: 2 additions & 2 deletions cli/commands/clean.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import ora from "ora"

import { defaultUIFolder } from "../info"
import { getComponentDependencyListFromPackage, getPackageInfo, getPackageManager } from "../utils/package"
import execa from "execa"
import { execa } from "execa"
import path from "path"
import { existsSync, promises as fs } from "fs"
import { logger } from "../utils/logger"
Expand All @@ -31,7 +31,7 @@ export const clean = new Command()
const spinner = ora(`Uninstalling component dependencies...`).start()

// Get only component dependencies that are installed in the project
let deps = getComponentDependencyListFromPackage()
let deps = await getComponentDependencyListFromPackage()


// DEVNOTE - Dev only
Expand Down
2 changes: 1 addition & 1 deletion cli/commands/init.ts
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ export const init = new Command()
* -----------------------------------------------------------------------------------------------*/

// Get available components
const availableComponents = getAvailableComponents()
const availableComponents = await getAvailableComponents()

if (!availableComponents?.length) {
logger.error("An error occurred while fetching components. Please try again.")
Expand Down
4 changes: 2 additions & 2 deletions cli/commands/remove.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { getAvailableComponentDependencyListFromDir, getAvailableComponents } fr
import process from "process"
import _ from "lodash"
import ora from "ora"
import execa from "execa"
import { execa } from "execa"
import path from "path"
import { existsSync, promises as fs } from "fs"
import { getProjectInfo } from "../helpers/project"
Expand Down Expand Up @@ -53,7 +53,7 @@ export const remove = new Command()
])

// Get available components
const availableComponents = getAvailableComponents()
const availableComponents = await getAvailableComponents()
const installedComponentDependencies = await getAvailableComponentDependencyListFromDir(dir)

if (!availableComponents?.length) {
Expand Down
2 changes: 1 addition & 1 deletion cli/commands/update.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ export const update = new Command()
const maintainStyling = false

// Get available components
const availableComponents = getAvailableComponents()
const availableComponents = await getAvailableComponents()
const installedComponents = await getInstalledComponents(dir)

console.log("")
Expand Down
6 changes: 3 additions & 3 deletions cli/helpers/add-components.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { DependencyDef } from "../helpers/dependencies"
import { DependencyDef } from "./dependencies"
import _ from "lodash"
import ora from "ora"
import fs from "fs"
import path from "path"
import { Component, getAvailableComponents, getAvailableComponentsFromDir } from "../helpers/components"
import { Component, getAvailableComponents, getAvailableComponentsFromDir } from "./components"
import chalk from "chalk"

export async function script_addComponents(
Expand All @@ -22,7 +22,7 @@ export async function script_addComponents(
}
) {

const availableComponents = getAvailableComponents()
const availableComponents = await getAvailableComponents()
const installedComponents = await getAvailableComponentsFromDir(componentDestination)

let componentsToAdd: Component[] = components
Expand Down
2 changes: 1 addition & 1 deletion cli/helpers/component-selection.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Component } from "../helpers/components"
import { Component } from "./components"
import prompts from "prompts"

export async function promptForComponents(components: Component[]) {
Expand Down
52 changes: 28 additions & 24 deletions cli/helpers/components.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,21 @@
import * as z from "zod"
import { createJSONSnapshot } from "../../scripts/create-bank-snapshot"
import components from "../bank/bank.json"
import fs from "fs"
import { HttpsProxyAgent } from "https-proxy-agent"
import _ from "lodash"
import fetch from "node-fetch"
import path from "path"
import * as z from "zod"
import { createBankSnapshot } from "../../scripts/create-bank-snapshot"

const URL = process.env.COMPONENTS_BANK_URL || "https://chalk.rahim.app"
const agent = process.env.https_proxy
? new HttpsProxyAgent(process.env.https_proxy)
: undefined

const componentSchema = z.object({
component: z.string(),
name: z.string(),
dependencies: z.array(z.array(z.string())).optional(),
family: z.array(z.string()).optional(),
dependencies: z.array(z.array(z.string())),
family: z.array(z.string()),
files: z.array(
z.object({
name: z.string(),
Expand All @@ -21,37 +27,35 @@ const componentSchema = z.object({

export type Component = z.infer<typeof componentSchema>

const componentsSchema = z.array(componentSchema)

/**
* Get all components from components.json file
* Get all components from the components bank
*/
export function getAvailableComponents() {
return components.map((component: Component) => {
return {
component: component.component,
name: component.name,
dependencies: component.dependencies,
family: component.family,
files: component.files,
}
})
export async function getAvailableComponents(): Promise<Component[]> {
try {
const res = await fetch(`${URL}/api/bank`, { agent })
console.log(URL)
return await res.json() as Component[]
}
catch (error) {
console.error(error)
throw new Error(`Failed to fetch components bank from ${URL}`)
}
}

/**
* Get the latest component list from components.js
* -> ['button', ...]
*/
export function getAvailableComponentDependencyList() {
const availableComponents = getAvailableComponents()
export async function getAvailableComponentDependencyList() {
const availableComponents = await getAvailableComponents()
return _.flatten(availableComponents.map(c => c.dependencies?.map(n => n[0]))).filter(n => n!.length > 0) as string[]
}

/**
* Get the latest version of already installed components
*/
export async function getAvailableComponentsFromDir(dir: string) {
const availableComponents = getAvailableComponents()
const availableComponents = await getAvailableComponents()
const installedComponents = await getInstalledComponentList(dir)
return installedComponents.map(name => availableComponents.filter(comp => comp.component === name)[0])
}
Expand All @@ -69,21 +73,21 @@ export async function getAvailableComponentDependencyListFromDir(dir: string) {
*/
export async function getInstalledComponents(dir: string) {
const srcPath = path.resolve(dir)
return await createJSONSnapshot(srcPath, path.resolve("./package.json"))
return await createBankSnapshot(srcPath, path.resolve("./package.json"))
}

/**
* Get the list of installed components
*/
export async function getInstalledComponentList(dir: string) {
try {

const directoryEntries = await fs.promises.readdir(dir, { withFileTypes: true })

return directoryEntries
.filter((entry) => entry.isDirectory())
.map((entry) => entry.name)
} catch (error) {
}
catch (error) {
console.error("Error occurred while reading directory names:", error)
return []
}
Expand Down
2 changes: 1 addition & 1 deletion cli/helpers/dependencies.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { getPackageManager } from "../utils/package"
import { logger } from "../utils/logger"
import execa from "execa"
import { execa } from "execa"
import ora from "ora"
import path from "path"
import fs from "fs-extra"
Expand Down
1 change: 0 additions & 1 deletion cli/helpers/update-components.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import _ from "lodash"


export function mergeFileContent(originalContent: string, updatedContent: string): string {
const originalLines = originalContent.split("\n")
const updatedLines = updatedContent.split("\n")
Expand Down
2 changes: 1 addition & 1 deletion cli/info.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export const defaultUIFolder = "./test/components/ui"
export const defaultUIFolder = "./src/components/ui"
export const mainDependencies: DependencyDef[] = [
["class-variance-authority", "^0.7.0", ""],
["clsx", "^2.1.0", ""],
Expand Down
Loading

0 comments on commit f6cab64

Please sign in to comment.