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

ramunarasinga

Ramu Narasinga

Posted on July 10, 2024

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

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.9, we looked at getRegistryStyles function, fetchRegistry function and stylesSchema.

In this article, we will understand the below concepts:

  1. getRegistryBaseColors function
  2. prompts
  3. Creating components.json
  4. resolveConfigPaths

getRegistryBaseColors

Unlike the getRegistryStyles function, getRegistryBaseColors does not use fetchRegistry function, it simply returns an array as shown below:



 export async function getRegistryBaseColors() {
  return \[
    {
      name: "slate",
      label: "Slate",
    },
    {
      name: "gray",
      label: "Gray",
    },
    {
      name: "zinc",
      label: "Zinc",
    },
    {
      name: "neutral",
      label: "Neutral",
    },
    {
      name: "stone",
      label: "Stone",
    },
  \]
}


Enter fullscreen mode Exit fullscreen mode

prompts

promptForMinimalConfig calls prompts with an array objects as in the below image.

Prompts is an npm package is an easy to use CLI prompts to enquire users for information. Prompts docs has a lot of examples, do check them out.

Based on the response that you provide in your CLI, it sets the style, baseColor and cssVariables.



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


Enter fullscreen mode Exit fullscreen mode

and these are used in setting the config.

Creating components.json

After setting the config, promptsForMinimalConfig creates components.json using this config.



// 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()


Enter fullscreen mode Exit fullscreen mode

fs.writeFile asynchronously writes data to a file, replacing the file if it already exists. data can be a string, a buffer, an , or an object. The promise is fulfilled with no arguments upon success.

JSON.stringify(config, null, 2)

We all have seen JSON.stringify() but what are these additional params, null and 2?

Reading the mdn docs for JSON.stringify, JSON.stringify has the below syntax:



JSON.stringify(value)
JSON.stringify(value, replacer)
JSON.stringify(value, replacer, space)


Enter fullscreen mode Exit fullscreen mode

This example below demonstrates perfectly



function replacer(key, value) {
  // Filtering out properties
  if (typeof value === "string") {
    return undefined;
  }
  return value;
}

const foo = {
  foundation: "Mozilla",
  model: "box",
  week: 45,
  transport: "car",
  month: 7,
};
JSON.stringify(foo, null, 2);


Enter fullscreen mode Exit fullscreen mode

resolveConfigPaths



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

  if (tsConfig.resultType === "failed") {
    throw new Error(
      \`Failed to load ${config.tsx ? "tsconfig" : "jsconfig"}.json. ${
        tsConfig.message ?? ""
      }\`.trim()
    )
  }

  return configSchema.parse({
    ...config,
    resolvedPaths: {
      tailwindConfig: path.resolve(cwd, config.tailwind.config),
      tailwindCss: path.resolve(cwd, config.tailwind.css),
      utils: await resolveImport(config.aliases\["utils"\], tsConfig),
      components: await resolveImport(config.aliases\["components"\], tsConfig),
      ui: config.aliases\["ui"\]
        ? await resolveImport(config.aliases\["ui"\], tsConfig)
        : await resolveImport(config.aliases\["components"\], tsConfig),
    },
  })
}


Enter fullscreen mode Exit fullscreen mode

resolveConfigPaths has the resolvedPaths object with some keys resolved using using path.resolve. Keys like tailwindConfig, tailwindCss, utils, components, ui are set.

Conclusion:

In this article, I discussed the following concepts:

  1. getRegistryBaseColors

Unlike the getRegistryStyles function, getRegistryBaseColors does not use fetchRegistry function, it simply returns an array

2. prompts

Prompts package lets you enquire users for information in the CLI. Prompts docs has a lot of examples, do check them out.

3. Creating components.json

promptsForMinimalConfig creates components.json using fs.writeFile

4. JSON.stringify(config, null, 2)

We all have seen JSON.stringify() but what are these additional params, null and 2 used in shadcn-ui/ui CLI source code?



 await fs.writeFile(targetPath, JSON.stringify(config, null, 2), "utf8")


Enter fullscreen mode Exit fullscreen mode

From the mdn docs, JSON.stringify can have any of the following syntax:



JSON.stringify(value)
JSON.stringify(value, replacer)
JSON.stringify(value, replacer, space)


Enter fullscreen mode Exit fullscreen mode

Get free courses inspired by the best practices used in open source.

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

Learn the best practices used in open source.

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/utils/registry/index.ts#L39
  3. https://github.com/shadcn-ui/ui/blob/main/packages/cli/src/utils/get-config.ts#L65
💖 💪 🙅 🚩
ramunarasinga
Ramu Narasinga

Posted on July 10, 2024

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related