Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(cli): A bit smarter cli #823

Closed
wants to merge 11 commits into from
11 changes: 7 additions & 4 deletions packages/cli/src/commands/init.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,12 @@
import { existsSync, promises as fs } from "fs"
import path from "path"
import {
DEFAULT_COMPONENTS,
DEFAULT_TAILWIND_CONFIG,
DEFAULT_TAILWIND_CSS,
DEFAULT_UTILS,
getConfig,
rawConfigSchema,
resolveConfigPaths,
type Config,
} from "@/src/utils/get-config"
import { getInitialValues } from "@/src/utils/get-initial-values"
import { getPackageManager } from "@/src/utils/get-package-manager"
import { handleError } from "@/src/utils/handle-error"
import { logger } from "@/src/utils/logger"
Expand Down Expand Up @@ -84,6 +81,12 @@ export async function promptForConfig(

const styles = await getRegistryStyles()
const baseColors = await getRegistryBaseColors()
const {
DEFAULT_COMPONENTS,
DEFAULT_TAILWIND_CONFIG,
DEFAULT_UTILS,
DEFAULT_TAILWIND_CSS,
} = await getInitialValues(cwd)

const options = await prompts([
{
Expand Down
9 changes: 1 addition & 8 deletions packages/cli/src/utils/get-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,6 @@ import { cosmiconfig } from "cosmiconfig"
import { loadConfig } from "tsconfig-paths"
import * as z from "zod"

export const DEFAULT_STYLE = "default"
export const DEFAULT_COMPONENTS = "@/components"
export const DEFAULT_UTILS = "@/lib/utils"
export const DEFAULT_TAILWIND_CSS = "app/globals.css"
export const DEFAULT_TAILWIND_CONFIG = "tailwind.config.js"
export const DEFAULT_TAILWIND_BASE_COLOR = "slate"

// TODO: Figure out if we want to support all cosmiconfig formats.
// A simple components.json file would be nice.
const explorer = cosmiconfig("components", {
Expand Down Expand Up @@ -61,7 +54,7 @@ export async function getConfig(cwd: string) {

export async function resolveConfigPaths(cwd: string, config: RawConfig) {
// Read tsconfig.json.
const tsConfig = await loadConfig(cwd)
const tsConfig = loadConfig(cwd)

if (tsConfig.resultType === "failed") {
throw new Error(
Expand Down
49 changes: 49 additions & 0 deletions packages/cli/src/utils/get-initial-values.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import path from "path"
import fs from "fs-extra"

import { getProjectInfo, resolveProjectDir } from "./get-project-info"
Bekacru marked this conversation as resolved.
Show resolved Hide resolved

export const getInitialValues = async (cwd: string) => {
const { srcDir, appDir, tsconfig, stylesDir } = await getProjectInfo(cwd)
const dir = resolveProjectDir(appDir, srcDir)
const cssDir = stylesDir ?? dir
const css = (await findCssFile(cssDir)) ?? "globals.css"
const paths = tsconfig?.compilerOptions?.paths
const alias = paths
? Object.keys(paths)
.find((key) => paths[key].toString().includes(dir))
?.split("/*")[0] ??
Object.keys(paths)[0].split("/")[0] ??
"@"
: dir
const twConfig = await findTwConfig("./")
return {
DEFAULT_TAILWIND_CSS: cssDir.length ? `${cssDir}/${css}` : css,
DEFAULT_COMPONENTS: alias.length ? `${alias}/components` : "components",
DEFAULT_UTILS: alias.length ? `${alias}/lib/utils` : "lib/utils",
DEFAULT_TAILWIND_BASE_COLOR: "slate",
DEFAULT_TAILWIND_CONFIG: twConfig,
DEFAULT_STYLE: "default",
}
}

//find common global css files
const findCssFile = async (dir: string) => {
Bekacru marked this conversation as resolved.
Show resolved Hide resolved
const files = await fs.readdir(dir || "./")
const cssFiles = files.filter((file) => file.endsWith(".css"))
const prioritizedKeywords = ["index", "global", "style", "tailwind", "main"]

const matchingFile = cssFiles.find((file) => {
return prioritizedKeywords.some((keyword) => file.includes(keyword))
})
if (matchingFile) {
return matchingFile
}

return null
}

const findTwConfig = async (dir: string) => {
const files = await fs.readdir(dir ?? "./")
return files.find((file) => file.includes("tailwind.config"))
}
51 changes: 38 additions & 13 deletions packages/cli/src/utils/get-project-info.ts
Original file line number Diff line number Diff line change
@@ -1,44 +1,69 @@
import { existsSync } from "fs"
import path from "path"
import fs from "fs-extra"
import { TsConfigJson } from "type-fest"

export async function getProjectInfo() {
export async function getProjectInfo(cwd: string) {
const info = {
tsconfig: null,
srcDir: false,
appDir: false,
stylesDir: null,
srcComponentsUiDir: false,
componentsUiDir: false,
}

try {
const tsconfig = await getTsConfig()

const tsconfig = await getTsConfig(cwd)
const stylesDir = getStylesDir(cwd)
return {
tsconfig,
srcDir: existsSync(path.resolve("./src")),
srcDir: existsSync(path.resolve(cwd, "./src")),
appDir:
existsSync(path.resolve("./app")) ||
existsSync(path.resolve("./src/app")),
srcComponentsUiDir: existsSync(path.resolve("./src/components/ui")),
componentsUiDir: existsSync(path.resolve("./components/ui")),
existsSync(path.resolve(cwd, "./app")) ||
existsSync(path.resolve(cwd, "./src/app")),
stylesDir,
srcComponentsUiDir: existsSync(path.resolve(cwd, "./src/components/ui")),
componentsUiDir: existsSync(path.resolve(cwd, "./components/ui")),
}
} catch (error) {
return info
}
}

export async function getTsConfig() {
export async function getTsConfig(cwd: string) {
try {
const tsconfigPath = path.join("tsconfig.json")
const tsconfigPath = path.join(cwd, "tsconfig.json")
const tsconfig = await fs.readJSON(tsconfigPath)

if (!tsconfig) {
throw new Error("tsconfig.json is missing")
}

return tsconfig
return tsconfig as TsConfigJson
} catch (error) {
return null
}
}

export function getStylesDir(cwd: string) {
const possiblePaths = [
"./styles",
"./src/styles",
"./src/app/styles",
"./app/styles",
]

const stylesDir =
possiblePaths.find((possiblePath) =>
existsSync(path.resolve(cwd, possiblePath))
) || null

return stylesDir
}

export function resolveProjectDir(appDir: boolean, srcDir: boolean) {
Bekacru marked this conversation as resolved.
Show resolved Hide resolved
if (appDir && srcDir) return "src/app"
if (appDir) return "app"
if (srcDir) return "src"

return ""
}