DEV Community

Cover image for shadcn-ui/ui codebase analysis: How does shadcn-ui CLI work? — Part 2.12
Ramu Narasinga
Ramu Narasinga

Posted on • Edited on

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

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.11, we looked at runInit function and how shadcn-ui/ui ensures directories provided in resolvedPaths in config exist.

The following operations are performed in runInit function:

  1. Ensure all resolved paths directories exist.
  2. Write tailwind config.
  3. Write css file.
  4. Write cn file.
  5. Install dependencies.

Let’s understand how shadcn-ui/ui CLI writes to tailwind config in runInit function.

Write tailwind config

After checking the directories exist, there are few more operations performed before writing tailwind config as shown in the below code.

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

const extension = config.tsx ? "ts" : "js"

const tailwindConfigExtension = path.extname(
  config.resolvedPaths.tailwindConfig
)

let tailwindConfigTemplate: string
if (tailwindConfigExtension === ".ts") {
  tailwindConfigTemplate = config.tailwind.cssVariables
    ? templates.TAILWIND\_CONFIG\_TS\_WITH\_VARIABLES
    : templates.TAILWIND\_CONFIG\_TS
} else {
  tailwindConfigTemplate = config.tailwind.cssVariables
    ? templates.TAILWIND\_CONFIG\_WITH\_VARIABLES
    : templates.TAILWIND\_CONFIG
}

// Write tailwind config.
await fs.writeFile(
  config.resolvedPaths.tailwindConfig,
  template(tailwindConfigTemplate)({
    extension,
    prefix: config.tailwind.prefix,
  }),
  "utf8"
)
Enter fullscreen mode Exit fullscreen mode

Let’s understand this code snippet

Extension

const extension = config.tsx ? "ts" : "js"
Enter fullscreen mode Exit fullscreen mode

This extension is used in later parts of code that deal with writing cn file.

tailwindConfigExtension

const tailwindConfigExtension = path.extname(
  config.resolvedPaths.tailwindConfig
)
Enter fullscreen mode Exit fullscreen mode

The path.extname() method returns the extension of the path, from the last occurrence of the . (period) character to end of string in the last portion of the path. If there is no . in the last portion of the path, or if there are no . characters other than the first character of the basename of path (see path.basename()) , an empty string is returned.

let tailwindConfigTemplate: string
if (tailwindConfigExtension === ".ts") {
  tailwindConfigTemplate = config.tailwind.cssVariables
    ? templates.TAILWIND\_CONFIG\_TS\_WITH\_VARIABLES
    : templates.TAILWIND\_CONFIG\_TS
} else {
  tailwindConfigTemplate = config.tailwind.cssVariables
    ? templates.TAILWIND\_CONFIG\_WITH\_VARIABLES
    : templates.TAILWIND\_CONFIG
}
Enter fullscreen mode Exit fullscreen mode

Depending on the tailwindConfigExtension and config.tailwind.cssVariables, tailwindConfigTemplate is set to a value using templates. Templates is imported from utils/templates and contains some variables initialised with values related to tailwind config

Write to tailwind config file

await fs.writeFile(
  config.resolvedPaths.tailwindConfig,
  template(tailwindConfigTemplate)({
    extension,
    prefix: config.tailwind.prefix,
  }),
  "utf8"
)
Enter fullscreen mode Exit fullscreen mode

template used here in the above snippet is different from utils/templates. Different how? this template is imported from lodash.template

Read more about lodash.template

An example using lodash.template:

// Use the "interpolate" delimiter to create a compiled template.
var compiled = \_.template('hello <%= user %>!');
compiled({ 'user': 'fred' });
// => 'hello fred!'
Enter fullscreen mode Exit fullscreen mode

This explains why there is %extension% in utils/templates.ts

It gets replaced with w/e is passed. Interesting…

Conclusion:

After checking the directories exist (explained in the part 2.11), there are few more operations performed before writing tailwind config as shown in the below code.

const extension = config.tsx ? "ts" : "js"

const tailwindConfigExtension = path.extname(
  config.resolvedPaths.tailwindConfig
)

let tailwindConfigTemplate: string
if (tailwindConfigExtension === ".ts") {
  tailwindConfigTemplate = config.tailwind.cssVariables
    ? templates.TAILWIND\_CONFIG\_TS\_WITH\_VARIABLES
    : templates.TAILWIND\_CONFIG\_TS
} else {
  tailwindConfigTemplate = config.tailwind.cssVariables
    ? templates.TAILWIND\_CONFIG\_WITH\_VARIABLES
    : templates.TAILWIND\_CONFIG
}

// Write tailwind config.
await fs.writeFile(
  config.resolvedPaths.tailwindConfig,
  template(tailwindConfigTemplate)({
    extension,
    prefix: config.tailwind.prefix,
  }),
  "utf8"
)
Enter fullscreen mode Exit fullscreen mode

extension — This extension is used in later parts of code that deal with writing cn file.

tailwindConfigExtension — The path.extname() method returns the extension of the path, from the last occurrence of the . (period) character to end of string in the last portion of the path.

Depending on the tailwindConfigExtension and config.tailwind.cssVariables, tailwindConfigTemplate is set to a value using templates. Templates is imported from utils/templates and contains some variables initialised with values related to tailwind config

Write to tailwind config file — using fs.writeFile, tailwindConfig is updated but there is a catch. template from lodash.template is used to perform some replacements before writing to the file.

An example usage of lodash.template from the docs:

// Use the "interpolate" delimiter to create a compiled template.
var compiled = \_.template('hello <%= user %>!');
compiled({ 'user': 'fred' });
// => 'hello fred!'
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#L331C3-L356C4
  2. https://github.com/shadcn-ui/ui/blob/main/packages/cli/src/utils/get-config.ts#L53
  3. https://github.com/shadcn-ui/ui/blob/main/packages/cli/src/utils/get-config.ts#L43
  4. https://github.com/shadcn-ui/ui/blob/main/packages/cli/src/utils/get-config.ts#L20
  5. https://github.com/shadcn-ui/ui/blob/main/packages/cli/src/utils/templates.ts
  6. https://lodash.com/docs#template

Top comments (0)