shadcn-ui/ui codebase analysis: How does shadcn-ui CLI work? — Part 2.8

shadcn-ui/ui codebase analysis: How does shadcn-ui CLI work? — Part 2.8

I wanted to find out how shadcn-ui CLI works. In this article, I discuss the code used to build the shadcn-ui/ui CLI.

In part 2.7, we looked at function isTypescriptProject this function checks if the cwd (current working directory) has a tsconfig.json file.

Let’s move on to the next line of code.

We looked at the implementation details of getProjectConfig in parts 2.1–2.7, lets come back to the cli/src/commands/init.ts.

Let’s understand the function promptForMinimalConfig function from this code snippet below:

if (projectConfig) {
  const config = await promptForMinimalConfig(
    cwd,
    projectConfig,
    opts.defaults
  )
  await runInit(cwd, config)
}

promptForMinimalConfig

This code below is picked from cli/commands/init.ts.

export async function promptForMinimalConfig(
  cwd: string,
  defaultConfig: Config,
  defaults = false
) {
  const highlight = (text: string) => chalk.cyan(text)
  let style = defaultConfig.style
  let baseColor = defaultConfig.tailwind.baseColor
  let cssVariables = defaultConfig.tailwind.cssVariables

  if (!defaults) {
    const styles = await getRegistryStyles()
    const baseColors = await getRegistryBaseColors()

    const options = await prompts([
      {
        type: "select",
        name: "style",
        message: `Which ${highlight("style")} would you like to use?`,
        choices: styles.map((style) => ({
          title: style.label,
          value: style.name,
        })),
      },
      {
        type: "select",
        name: "tailwindBaseColor",
        message: `Which color would you like to use as ${highlight(
          "base color"
        )}?`,
        choices: baseColors.map((color) => ({
          title: color.label,
          value: color.name,
        })),
      },
      {
        type: "toggle",
        name: "tailwindCssVariables",
        message: `Would you like to use ${highlight(
          "CSS variables"
        )} for colors?`,
        initial: defaultConfig?.tailwind.cssVariables,
        active: "yes",
        inactive: "no",
      },
    ])

    style = options.style
    baseColor = options.tailwindBaseColor
    cssVariables = options.tailwindCssVariables
  }

  const config = rawConfigSchema.parse({
    $schema: defaultConfig?.$schema,
    style,
    tailwind: {
      ...defaultConfig?.tailwind,
      baseColor,
      cssVariables,
    },
    rsc: defaultConfig?.rsc,
    tsx: defaultConfig?.tsx,
    aliases: defaultConfig?.aliases,
  })

  // Write to file.
  logger.info("")
  const spinner = ora(`Writing components.json...`).start()
  const targetPath = path.resolve(cwd, "components.json")
  await fs.writeFile(targetPath, JSON.stringify(config, null, 2), "utf8")
  spinner.succeed()

  return await resolveConfigPaths(cwd, config)
}

Okay, there is a lot going on here, let’s break this down by understanding small chunks of code.

Parameters:

promptForMinimalConfig function is called with the parameters named cwd — this is the current working directory, projectConfig — this is the value returned from getProjectConfig, opts.defaults — this is a CLI argument configured with Commander.js, shown below:

export const init = new Command()
  .name("init")
  .description("initialize your project and install dependencies")
  .option("-y, --yes", "skip confirmation prompt.", false)
  .option("-d, --defaults,", "use default configuration.", false)
  .option(
    "-c, --cwd <cwd>",
    "the working directory. defaults to the current directory.",
    process.cwd()
  )

Chalk

const highlight = (text: string) => chalk.cyan(text)

This line above is picked from packages/cli/src/commands/init.ts#L232. highlight is a small util function that applies a color to a text using chalk to be displayed in your terminal.

Find more details here about chalk package.

In the next few lines of code in promptForMinimalConfig style, baseColor and cssVariables are found to be set from defaultConfig.

let style = defaultConfig.style
let baseColor = defaultConfig.tailwind.baseColor
let cssVariables = defaultConfig.tailwind.cssVariables

getRegistryStyles

if there is no option like -d or — defaults passed in via your CLI when you run init command, there are few things to be sorted in an if block

if (!defaults) {
    const styles = await getRegistryStyles()

getRegistryStyles is imported from utils/registry/index.ts#L29.

export async function getRegistryStyles() {
  try {
    const [result] = await fetchRegistry(["styles/index.json"])

    return stylesSchema.parse(result)
  } catch (error) {
    throw new Error(`Failed to fetch styles from registry.`)
  }
}

This calls a function named fetchRegistry. More on this in the next article.

Conclusion:

Now that getProjectConfig function from shadcn-ui/ui CLI source code analysis is complete, I discussed few more lines of code that followed this function.

There’s a function named promptForMinimalConfig

export async function promptForMinimalConfig(
  cwd: string,
  defaultConfig: Config,
  defaults = false
) {
  const highlight = (text: string) => chalk.cyan(text)
  let style = defaultConfig.style
  let baseColor = defaultConfig.tailwind.baseColor
  let cssVariables = defaultConfig.tailwind.cssVariables

This function has the prompts such as Which color would you like to use as base color, Which color would you like to use as base color and Would you like to use CSS variables. I haven’t exactly reached that part of code yet to discuss how these prompts are configured but found that highlight is a small util function that applies color to the text that you see in your terminal using chalk package.

Want to learn how to build shadcn-ui/ui from scratch? Check out build-from-scratch

About me:

Website: https://ramunarasinga.com/

Linkedin: https://www.linkedin.com/in/ramu-narasinga-189361128/

Github: https://github.com/Ramu-Narasinga

Email: ramu.narasinga@gmail.com

Build shadcn-ui/ui from scratch

References:

  1. https://github.com/shadcn-ui/ui/blob/main/packages/cli/src/commands/init.ts#L232

  2. https://github.com/shadcn-ui/ui/blob/main/packages/cli/src/commands/init.ts#L70C7-L77C8

  3. https://github.com/shadcn-ui/ui/blob/main/packages/cli/src/utils/registry/index.ts#L29